Topik ini membahas dua verba dplyr yang bekerja berpasangan, yaitu summarize() untuk meringkas tabel menjadi statistik agregat dan group_by() untuk mengelompokkan baris agar perhitungan tersebut dijalankan per kelompok. Keduanya membentuk pola split-apply-combine, yaitu memecah data menjadi kelompok, menerapkan satu perhitungan pada setiap kelompok, lalu menggabungkan hasilnya kembali. Pembahasan ditutup dengan penyusunan rantai verba lengkap dalam satu pipe. Dataset contoh didefinisikan ulang di awal topik agar seluruh contoh tetap dapat dijalankan ulang secara mandiri.
library(tidyverse)
nilai <- tibble::tibble(
nama = c("Ali", "Budi", "Citra", "Dewi", "Eka", "Fajar", "Gita", "Hana"),
kelas = c("7A", "7B", "7A", "7B", "7A", "7B", "7A", "7B"),
nilai = c(88, 74, 92, 65, 81, 79, 95, 70),
kehadiran = c(0.95, 0.88, 1.00, 0.75, 0.92, 0.85, 0.98, 0.80)
)── Attaching core tidyverse packages ──────────────────────── tidyverse 2.0.0 ──
✔ dplyr 1.2.1 ✔ readr 2.2.0
✔ forcats 1.0.1 ✔ stringr 1.6.0
✔ ggplot2 4.0.3 ✔ tibble 3.3.1
✔ lubridate 1.9.5 ✔ tidyr 1.3.2
✔ purrr 1.2.2
── Conflicts ────────────────────────────────────────── tidyverse_conflicts() ──
✖ dplyr::filter() masks stats::filter()
✖ dplyr::lag() masks stats::lag()
ℹ Use the conflicted package (<http://conflicted.r-lib.org/>) to force all conflicts to become errorsMerangkum tabel dengan summarize()#
Verba summarize() menerima tabel sebagai argumen pertamanya, lalu mengembalikan tabel baru yang berisi satu baris dengan kolom-kolom hasil perhitungan. Setiap argumennya berupa ekspresi penugasan sebagaimana mutate(), tetapi ekspresinya harus menghasilkan satu nilai, biasanya berupa fungsi statistik yang diterapkan pada satu kolom.
nilai |>
summarize(
rata = mean(nilai),
sd = sd(nilai),
terendah = min(nilai),
tertinggi = max(nilai)
)# A tibble: 1 × 4
rata sd terendah tertinggi
<dbl> <dbl> <dbl> <dbl>
1 80.5 10.7 65 95Hasilnya satu baris yang meringkas seluruh baris tabel asalnya, yaitu rata-rata 80,5 dengan simpangan baku sekitar 10,7. Fungsi statistik yang dipakai adalah fungsi bawaan R seperti mean(), sd(), min(), dan max(), sehingga verba summarize() sendiri tidak menghitung apa pun dan hanya mengatur cara hasilnya tersusun menjadi tabel.
Penghitung banyaknya baris dan nilai unik#
Dua fungsi khusus sering dipakai di dalam summarize() karena maknanya bergantung pada tabel yang sedang diringkas. Fungsi n() menghitung banyaknya baris tanpa memerlukan argumen, sedangkan n_distinct() menghitung banyaknya nilai berbeda pada kolom yang disebutkan.
nilai |>
summarize(
banyak_baris = n(),
banyak_kelas = n_distinct(kelas),
banyak_nama = n_distinct(nama)
)# A tibble: 1 × 3
banyak_baris banyak_kelas banyak_nama
<int> <int> <int>
1 8 2 8Hasilnya menyatakan bahwa tabel memuat delapan baris, dua kelas, dan delapan nama yang semuanya berbeda. Apabila data berisi nama ganda, n_distinct(nama) tetap menghitung sekali untuk setiap nama, sehingga hasilnya bisa lebih kecil daripada n().
Pengelompokan dengan group_by()#
Fungsi group_by() tidak mengubah isi tabel maupun urutan barisnya, melainkan menandai bahwa baris-barisnya terbagi menjadi kelompok-kelompok menurut kolom yang disebutkan. Tanda pengelompokan itu terlihat pada cetakannya.
per_kelas <- nilai |> group_by(kelas)
per_kelas# A tibble: 8 × 4
# Groups: kelas [2]
nama kelas nilai kehadiran
<chr> <chr> <dbl> <dbl>
1 Ali 7A 88 0.95
2 Budi 7B 74 0.88
3 Citra 7A 92 1
4 Dewi 7B 65 0.75
5 Eka 7A 81 0.92
6 Fajar 7B 79 0.85
7 Gita 7A 95 0.98
8 Hana 7B 70 0.8 Baris Groups: kelas [2] menyatakan bahwa tabel tergrup menjadi dua kelompok menurut kolom kelas. Apabila summarize() dijalankan pada tabel tergrup, perhitungannya dijalankan pada setiap kelompok secara terpisah, kemudian hasilnya digabung menjadi satu baris untuk setiap kelompok. Inilah pola split-apply-combine pada praktiknya, yaitu kelas 7A dan 7B dipecah, statistiknya dihitung masing-masing, lalu hasilnya disatukan. Perhitungan hanya ditulis satu kali untuk seluruh kelompok, tanpa memilah baris secara manual untuk setiap kelas.
per_kelas |>
summarize(
banyak_siswa = n(),
rata_nilai = mean(nilai),
sd_nilai = sd(nilai)
)# A tibble: 2 × 4
kelas banyak_siswa rata_nilai sd_nilai
<chr> <int> <dbl> <dbl>
1 7A 4 89 6.06
2 7B 4 72 5.94Fungsi n() kini menghasilkan banyaknya baris per kelompok, yaitu empat siswa untuk masing-masing kelas, dan rata-rata dihitung terpisah untuk 7A dan 7B. Hasil keluaran di atas juga tidak lagi memuat baris Groups, karena summarize() melepas satu lapis pengelompokan setelah perhitungannya selesai. Apabila pengelompokannya bertingkat dengan dua kolom, satu lapis dilepas dan sisanya tetap menempel pada hasilnya.
Melepas pengelompokan dengan ungroup()#
Tabel tergrup yang disimpan dalam variabel tetap tergrup sampai pengelompokannya dilepas secara eksplisit. Fungsi ungroup() menghapus tanda kelompok tersebut dan mengembalikan tabel biasa tanpa mengubah isinya.
per_kelas |> ungroup()# A tibble: 8 × 4
nama kelas nilai kehadiran
<chr> <chr> <dbl> <dbl>
1 Ali 7A 88 0.95
2 Budi 7B 74 0.88
3 Citra 7A 92 1
4 Dewi 7B 65 0.75
5 Eka 7A 81 0.92
6 Fajar 7B 79 0.85
7 Gita 7A 95 0.98
8 Hana 7B 70 0.8 Dampaknya terlihat pada perhitungan yang melibatkan seluruh tabel. Ekspresi berikut dimaksudkan menghitung porsi setiap siswa terhadap jumlah nilai seluruh tabel, tetapi tabel per_kelas masih tergrup, sehingga sum(nilai) dihitung di dalam setiap kelompoknya.
per_kelas |>
mutate(bagian = nilai / sum(nilai))# A tibble: 8 × 5
# Groups: kelas [2]
nama kelas nilai kehadiran bagian
<chr> <chr> <dbl> <dbl> <dbl>
1 Ali 7A 88 0.95 0.247
2 Budi 7B 74 0.88 0.257
3 Citra 7A 92 1 0.258
4 Dewi 7B 65 0.75 0.226
5 Eka 7A 81 0.92 0.228
6 Fajar 7B 79 0.85 0.274
7 Gita 7A 95 0.98 0.267
8 Hana 7B 70 0.8 0.243Porsi Ali yang bernilai 88 dihitung terhadap 356, yaitu jumlah nilai kelas 7A, dan bukan terhadap 644 sebagai jumlah seluruh tabel. Perilaku ini bisa dimanfaatkan apabila memang diinginkan, tetapi menjadi jebakan apabila pengelompokannya tersisa tanpa sengaja. Pemanggilan ungroup() lebih dahulu menghasilkan porsi terhadap jumlah seluruh tabel.
Rantai verba dalam satu pipe#
Seluruh verba pada modul ini dapat disambung dalam satu rantai pipe, dan kombinasi group_by() dengan summarize() biasanya menjadi intinya. Rantai berikut menghitung statistik per kelas, lalu mengurutkan hasilnya dari rata-rata tertinggi.
nilai |>
group_by(kelas) |>
summarize(
banyak_siswa = n(),
rata_nilai = mean(nilai)
) |>
arrange(desc(rata_nilai))# A tibble: 2 × 3
kelas banyak_siswa rata_nilai
<chr> <int> <dbl>
1 7A 4 89
2 7B 4 72Verba pada rantai itu dijalankan berurutan, yaitu pengelompokan, peringkasan, lalu pengurutan, sehingga arrange() bekerja pada tabel hasil peringkasan yang kolomnya baru saja dibentuk. Verba filter() juga dapat disisipkan untuk memilih kelompok yang hasil agregatnya memenuhi kondisi tertentu.
nilai |>
group_by(kelas) |>
summarize(
banyak_siswa = n(),
rata_nilai = mean(nilai),
rata_kehadiran = mean(kehadiran)
) |>
filter(rata_nilai >= 75) |>
arrange(desc(rata_kehadiran))# A tibble: 1 × 4
kelas banyak_siswa rata_nilai rata_kehadiran
<chr> <int> <dbl> <dbl>
1 7A 4 89 0.962Kelas 7B tersaring karena rata-rata nilainya 72 berada di bawah batas 75, sehingga yang tersisa hanya kelas 7A dengan rata-rata kehadiran 0,962. Kondisi pada filter() di sini menguji kolom hasil agregat, bukan baris data mentahnya, dan pemisahan dua tahap inilah yang membuat statistik per kelompok dapat dibangun bertingkat dengan rapi. Rantai semacam ini juga mudah diperluas, misalnya menambah kolom agregat lain pada summarize() tanpa mengubah tahapan lainnya.
Ringkasan#
summarize()meringkas tabel menjadi satu baris per kelompok, dengan setiap kolom hasilnya berupa fungsi statistik sepertimean(),sd(),min(), ataumax()yang menghasilkan satu nilai.n()menghitung banyaknya baris pada kelompoknya, sedangkann_distinct()menghitung banyaknya nilai berbeda pada kolom yang disebutkan.group_by()menandai pembagian kelompok tanpa mengubah isi tabel, dansummarize()pada tabel tergrup menjalankan pola split-apply-combine.summarize()melepas satu lapis pengelompokan, sedangkanungroup()melepas seluruhnya, dan tabel tergrup yang lupa dilepas membuat verba berikutnya bekerja per kelompok.- Rantai
group_by(),summarize(),filter(), danarrange()menyusun perhitungan per kelompok secara bertingkat dalam satu pipe.
Referensi#
- dplyr: A Grammar of Data Manipulation — tidyverse
— dokumentasi resmi
summarize(),group_by(), danungroup()beserta perilaku tabel tergrupnya. - R for Data Science, edisi ke-2 — Wickham, Çetinkaya-Rundel, Grolemund — bab transformasi data menjelaskan pola grouped summaries beserta jebakannya.