Frekuensi / proporsi relatif dengan dplyr

Misalkan saya ingin menghitung proporsi nilai yang berbeda dalam setiap kelompok. Misalnya, dengan menggunakan data mtcars, bagaimana cara menghitung frekuensi relatif jumlah roda gigi menurut am (otomatis/manual) sekaligus dengan 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

person jenswirf    schedule 04.07.2014    source sumber
comment
Apakah persentase tersebut merupakan angka sebenarnya yang Anda inginkan? Dari mana asalnya secara aljabar? Ah, 79% adalah 15/(15+4), 21% adalah 4/(15+4) lalu untuk am==1 62% adalah 8/(8+5) dst.   -  person Spacedman    schedule 04.07.2014
comment
@Spacedman Ya, itu adalah angka yang saya inginkan dan Frank benar, jumlahnya 100% dengan variabel am (79+21) dan (62+38)..   -  person jenswirf    schedule 04.07.2014
comment
Tampaknya ini benar-benar mencari implementasi dplyr asli prop.table()/sweep(). Selain itu, dalam pertanyaan lain beberapa orang meminta opsi untuk menyertakan jumlah nol untuk variabel atau interaksi variabel   -  person smci    schedule 27.04.2016


Jawaban (10)


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 dplyr vignette :

Saat Anda mengelompokkan berdasarkan beberapa variabel, setiap ringkasan akan memisahkan satu tingkat pengelompokan. Hal ini memudahkan untuk menggabungkan kumpulan data secara bertahap.

Jadi, setelah summarise, variabel pengelompokan terakhir yang ditentukan dalam group_by, 'roda gigi', dihilangkan. Pada langkah mutate, data dikelompokkan berdasarkan variabel pengelompokan yang tersisa, di sini 'am'. Anda dapat memeriksa pengelompokan di setiap langkah dengan groups.

Hasil dari pengelupasan tentu saja bergantung pada urutan variabel pengelompokan dalam panggilan group_by. Anda mungkin ingin melakukan group_by(am) berikutnya, untuk membuat kode Anda lebih eksplisit.

Untuk pembulatan dan prettifikasi, silakan lihat jawaban bagus dari @Tyler Rinker.

person Henrik    schedule 04.07.2014
comment
Saya baru saja menemukan solusi itu juga, tetapi saya tidak tahu mengapa sum(n) bekerja pada grup am dan bukan pada grup gear juga... - person Spacedman; 04.07.2014
comment
Lihat sketsa: Saat Anda mengelompokkan berdasarkan beberapa variabel, masing-masing ringkasan mengelupas satu tingkat pengelompokan. - person Henrik; 04.07.2014
comment
Bagus - jika Anda berhenti setelah summarise, akan disebutkan grup mana yang tersisa. Oh dplyr batu... - person Spacedman; 04.07.2014
comment
Sederhana dan jelas. Saya belum pernah mengetahui teori peel off sebelumnya, terima kasih! - person Shixiang Wang; 07.07.2019
comment
Bagus. sederhana dan efektif. kerja bagus! - person user2550228; 06.07.2020

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

  • dplyr 0.7.1: mengembalikan tabel tidak dikelompokkan: Anda perlu mengelompokkannya lagi berdasarkan am

  • dplyr ‹ 0.7.1: mengembalikan tabel yang dikelompokkan, jadi tidak perlu mengelompokkan lagi, meskipun Anda mungkin ingin ungroup() untuk manipulasi 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 dikelompokkan dengan ungroup().

person Matifou    schedule 08.08.2016
comment
Sepertinya ini jawaban yang tidak valid pada dplyr 0.7.1. Ia melakukan penghitungan frekuensi secara keseluruhan pada roda gigi, bukan pada setiap level am. - person Edwin; 19.07.2017

@ 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%
person Tyler Rinker    schedule 04.07.2014
comment
Anda selalu dapat membuat kelas persentase S3 dengan metode format yang menambahkan tanda persen... #overkill - person Spacedman; 04.07.2014
comment
Menerapkan ini mungkin menarik juga: stackoverflow.com/questions/13483430/ - person Spacedman; 04.07.2014
comment
Bagaimana jika seseorang menghitung mean, sd dan SE juga dalam contoh ini? - person user3655531; 16.01.2017

Saya menulis fungsi kecil untuk tugas 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

Ini 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
person slhck    schedule 18.03.2019

Meskipun banyak jawaban, satu pendekatan lagi yang menggunakan prop.table dikombinasikan dengan dplyr atau 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
Sejauh ini merupakan pendekatan paling sederhana - person Parseltongue; 25.01.2020

Berikut adalah fungsi umum yang mengimplementasikan solusi Henrik pada 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` tidak dapat dikonversi dari numerik ke karakter` - person f0nzie; 07.08.2018

Berikut adalah jawaban basis R menggunakan aggregate dan 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 

Kita juga dapat menggunakan prop.table tetapi keluarannya berbeda.

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

Demi kelengkapan pertanyaan populer ini, sejak dplyr versi 1.0.0, parameter .groups mengontrol struktur pengelompokan fungsi summarise setelah group_by bantuan rangkum.

Dengan .groups = "drop_last", summarise menghilangkan level pengelompokan terakhir. Ini adalah satu-satunya hasil yang diperoleh sebelum versi 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

Dengan .groups = "drop", semua level pengelompokan dihilangkan. Hasilnya berubah menjadi tibble mandiri tanpa jejak group_by sebelumnya

# .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%

Jika .groups = "keep", struktur pengelompokan sama dengan .data (mtcars, dalam hal ini). summarise tidak menghilangkan variabel apa pun yang digunakan di group_by.

Terakhir, dengan .groups = "rowwise", setiap baris adalah grupnya sendiri. Ini setara dengan tetap berada dalam situasi ini

# .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

Hal lain yang menarik adalah terkadang, setelah menerapkan group_by dan summarise, baris ringkasan dapat membantu.

# 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%

Dibuat pada 09-11-2020 oleh paket reprex (v0.3.0)

Semoga jawaban ini bermanfaat bagi Anda.

person josep maria porrà    schedule 09.11.2020

Jawaban ini didasarkan pada jawaban Matifou.

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

Lalu saya kalikan jawabannya dengan 100 untuk mendapatkan persen, bukan desimal, agar kolom frekuensi lebih mudah dibaca sebagai persentase.

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

Juga, coba add_count() (untuk menyiasati group_by .groups yang mengganggu)

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