Topik ini membahas verba mutate() dari dplyr, yaitu verba untuk membuat kolom baru dan mengubah isi kolom yang sudah ada pada data frame. Pembahasan mencakup pembuatan kolom yang hasilnya diturunkan dari kolom lain, kategorisasi baris dengan ifelse() dan case_when(), pengubahan kolom lama, pengaturan posisi kolom baru, penyebutan singkat across() untuk menerapkan satu fungsi pada banyak kolom, serta pola penyimpanan hasilnya. 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 errorsMembuat kolom baru#
Setiap argumen mutate() berupa satu ekspresi penugasan yang membentuk satu kolom baru, dan nama di sisi kiri tanda = menjadi nama kolomnya. Ekspresinya dievaluasi pada setiap baris sekaligus sebagaimana operasi vektor, sehingga satu baris kode menghitung satu kolom penuh. Contoh berikut menambahkan rata-rata kelas dan selisih nilai setiap siswa terhadap rata-rata itu.
nilai |>
mutate(
rata_kelas = mean(nilai),
selisih = nilai - rata_kelas
)# A tibble: 8 × 6
nama kelas nilai kehadiran rata_kelas selisih
<chr> <chr> <dbl> <dbl> <dbl> <dbl>
1 Ali 7A 88 0.95 80.5 7.5
2 Budi 7B 74 0.88 80.5 -6.5
3 Citra 7A 92 1 80.5 11.5
4 Dewi 7B 65 0.75 80.5 -15.5
5 Eka 7A 81 0.92 80.5 0.5
6 Fajar 7B 79 0.85 80.5 -1.5
7 Gita 7A 95 0.98 80.5 14.5
8 Hana 7B 70 0.8 80.5 -10.5Kolom selisih merujuk kolom rata_kelas yang didefinisikan lebih dahulu dalam pemanggilan yang sama, dan R menghitungnya berurutan sesuai urutan argumen. Nama kolom baru tidak boleh merujuk kolom yang belum dihitung, sebagaimana aturan pembuatan tibble pada modul struktur data. Tabel asalnya tidak berubah sama sekali, sedangkan hasilnya berupa tabel baru dengan dua kolom tambahan.
Membentuk kolom nilai akhir#
Kolom baru lazim menampung rumus yang menggabungkan beberapa kolom. Contoh berikut membentuk nilai akhir dengan bobot 70 persen nilai ujian dan 30 persen kehadiran. Kolom kehadiran bertipe proporsi antara 0 dan 1, sehingga dikalikan 30 agar skalanya menyatu dengan nilai ujian.
nilai |>
mutate(nilai_akhir = 0.7 * nilai + 30 * kehadiran)# A tibble: 8 × 5
nama kelas nilai kehadiran nilai_akhir
<chr> <chr> <dbl> <dbl> <dbl>
1 Ali 7A 88 0.95 90.1
2 Budi 7B 74 0.88 78.2
3 Citra 7A 92 1 94.4
4 Dewi 7B 65 0.75 68
5 Eka 7A 81 0.92 84.3
6 Fajar 7B 79 0.85 80.8
7 Gita 7A 95 0.98 95.9
8 Hana 7B 70 0.8 73 Perhitungan pada setiap baris bersifat independen dan memperhitungkan kedua komponennya. Eka yang bernilai 81 dan hadir 0,92 memperoleh 84,3, sedangkan Hana yang bernilai 70 dan hadir 0,80 memperoleh 73. Rumus semacam ini ditulis sekali di dalam mutate() dan langsung berlaku pada seluruh baris, tanpa perulangan maupun penulisan manual satu per satu.
Mengubah kolom yang sudah ada#
Apabila nama di sisi kiri tanda = sudah dimiliki tabel, mutate() menimpa isi kolom lama dengan hasil ekspresinya, sedangkan banyak kolomnya tetap sama. Contoh berikut menaikkan seluruh nilai ujian dua poin.
nilai |> mutate(nilai = nilai + 2)# A tibble: 8 × 4
nama kelas nilai kehadiran
<chr> <chr> <dbl> <dbl>
1 Ali 7A 90 0.95
2 Budi 7B 76 0.88
3 Citra 7A 94 1
4 Dewi 7B 67 0.75
5 Eka 7A 83 0.92
6 Fajar 7B 81 0.85
7 Gita 7A 97 0.98
8 Hana 7B 72 0.8 Kolom nilai pada hasil kini memuat 90, 76, 94, dan seterusnya, yaitu isi kolom lama yang ditambah dua. Ekspresi di sisi kanan dibaca menggunakan isi kolom lama sebelum penimpaan terjadi, sehingga tidak ada kebingungan antara nilai baru dan lama. Penimpaan bersifat permanen hanya apabila hasilnya disimpan kembali, dan pola penyimpanannya dibahas di akhir topik ini.
Kategorisasi dengan ifelse() dan case_when()#
Kolom kategorikal dibentuk dengan fungsi pengondisi yang menghasilkan satu label untuk setiap baris. Fungsi ifelse() menyelesaikan dua cabang, yaitu satu nilai untuk kondisi TRUE dan satu nilai untuk kondisi FALSE. Apabila cabangnya lebih dari dua, fungsi case_when() dari dplyr lebih mudah dibaca karena setiap pasangan kondisi dan hasil ditulis dengan formula kondisi ~ hasil.
nilai |>
mutate(
status = ifelse(nilai >= 75, "tuntas", "remedial"),
predikat = case_when(
nilai >= 90 ~ "A",
nilai >= 80 ~ "B",
nilai >= 70 ~ "C",
TRUE ~ "D"
)
)# A tibble: 8 × 6
nama kelas nilai kehadiran status predikat
<chr> <chr> <dbl> <dbl> <chr> <chr>
1 Ali 7A 88 0.95 tuntas B
2 Budi 7B 74 0.88 remedial C
3 Citra 7A 92 1 tuntas A
4 Dewi 7B 65 0.75 remedial D
5 Eka 7A 81 0.92 tuntas B
6 Fajar 7B 79 0.85 tuntas C
7 Gita 7A 95 0.98 tuntas A
8 Hana 7B 70 0.8 remedial C Kondisi pada case_when() diuji berurutan dari atas ke bawah, dan hasil pertama yang kondisinya terpenuhi itulah yang dipakai. Butir TRUE ~ "D" menampung seluruh baris yang lolos dari tiga kondisi di atasnya sehingga berperan sebagai pilihan bawaan. Citra bernilai 92 memperoleh A karena kondisi pertamanya terpenuhi, meskipun kondisi nilai >= 80 juga benar untuknya.
Mengatur posisi kolom baru#
Kolom baru secara bawaan ditempatkan di posisi paling kanan. Argumen .before dan .after menggeser posisi itu, misalnya menempatkan kolom selisih tepat di belakang kolom nilai agar perbandingannya berdampingan.
nilai |>
mutate(selisih = nilai - mean(nilai), .after = nilai)# A tibble: 8 × 5
nama kelas nilai selisih kehadiran
<chr> <chr> <dbl> <dbl> <dbl>
1 Ali 7A 88 7.5 0.95
2 Budi 7B 74 -6.5 0.88
3 Citra 7A 92 11.5 1
4 Dewi 7B 65 -15.5 0.75
5 Eka 7A 81 0.5 0.92
6 Fajar 7B 79 -1.5 0.85
7 Gita 7A 95 14.5 0.98
8 Hana 7B 70 -10.5 0.8 Pengaturan posisi hanya memengaruhi tata letak kolom dan tidak mengubah isinya. Nilai .before = 1 menempatkan kolom baru di posisi paling kiri, sedangkan nama kolom juga dapat dipakai sebagai acuan posisi.
Penerapan satu fungsi pada banyak kolom#
Apabila fungsi yang sama perlu diterapkan pada beberapa kolom sekaligus, across() menggabungkan pemilihan kolom dengan fungsinya dalam satu pemanggilan mutate(). Pemilihan kolomnya memakai mekanisme yang sama dengan select(), sehingga nama tunggal, rentang, dan fungsi bantu seperti where() semuanya berlaku.
nilai |>
mutate(across(where(is.character), toupper))# 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 Fungsi toupper diterapkan pada kedua kolom teks, yaitu nama dan kelas, tanpa menyentuh kolom numerik. across() juga menerima beberapa fungsi sekaligus dan menghasilkan satu kolom hasil untuk setiap fungsinya, sehingga penyebutan singkat ini cukup untuk mengenalinya dan detailnya dapat ditelusuri pada dokumentasi resminya.
Menyimpan hasil mutate#
Hasil mutate() hilang begitu saja apabila tidak disimpan, karena semua verba dplyr mengembalikan tabel baru tanpa mengubah tabel asalnya. Setelah seluruh contoh di atas, tabel nilai masih memuat empat kolom asalnya.
names(nilai)[1] "nama" "kelas" "nilai" "kehadiran"Untuk mempertahankan hasilnya, tabel yang dikembalikan disimpan kembali ke variabel, baik ke variabel baru maupun menimpa yang lama. Penimpaan memang lebih ringkas, tetapi menyimpan ke variabel baru memudahkan pemeriksaan ulang perhitungan bila hasilnya ternyata tidak sesuai dugaan.
nilai_lengkap <- nilai |>
mutate(nilai_akhir = 0.7 * nilai + 30 * kehadiran)
names(nilai_lengkap)[1] "nama" "kelas" "nilai" "kehadiran" "nilai_akhir"Ringkasan#
mutate()membentuk tabel baru dengan kolom tambahan dari ekspresi penugasan, dan kolom yang sama namanya ditimpa isinya.- Kolom baru boleh merujuk kolom lain yang didefinisikan lebih dahulu dalam pemanggilan yang sama.
ifelse()menyelesaikan dua cabang, sedangkancase_when()menyelesaikan banyak cabang dengan urutan kondisi yang menentukan hasilnya.- Argumen
.beforedan.aftermengatur posisi kolom baru, danacross()menerapkan satu fungsi pada beberapa kolom terpilih sekaligus. - Hasil
mutate()harus disimpan kembali dengan penugasan agar tidak hilang, karena tabel asalnya tidak pernah berubah.
Referensi#
- dplyr: A Grammar of Data Manipulation — tidyverse
— dokumentasi resmi
mutate(),case_when(), danacross()beserta argumen posisi kolomnya. - R for Data Science, edisi ke-2 — Wickham, Çetinkaya-Rundel, Grolemund — bab transformasi data memperkenalkan mutate beserta pola pembuatan kolom turunannya.