mutate

Membuat kolom baru dan mentransformasi kolom yang sudah ada.

Daftar isi

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.

r
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)
)
text Keluaran
── 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 errors

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

r
nilai |>
  mutate(
    rata_kelas = mean(nilai),
    selisih = nilai - rata_kelas
  )
text Keluaran
# 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.5

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

r
nilai |>
  mutate(nilai_akhir = 0.7 * nilai + 30 * kehadiran)
text Keluaran
# 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.

r
nilai |> mutate(nilai = nilai + 2)
text Keluaran
# 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.

r
nilai |>
  mutate(
    status = ifelse(nilai >= 75, "tuntas", "remedial"),
    predikat = case_when(
      nilai >= 90 ~ "A",
      nilai >= 80 ~ "B",
      nilai >= 70 ~ "C",
      TRUE ~ "D"
    )
  )
text Keluaran
# 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.

r
nilai |>
  mutate(selisih = nilai - mean(nilai), .after = nilai)
text Keluaran
# 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.

r
nilai |>
  mutate(across(where(is.character), toupper))
text Keluaran
# 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.

r
names(nilai)
text Keluaran
[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.

r
nilai_lengkap <- nilai |>
  mutate(nilai_akhir = 0.7 * nilai + 30 * kehadiran)
names(nilai_lengkap)
text Keluaran
[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, sedangkan case_when() menyelesaikan banyak cabang dengan urutan kondisi yang menentukan hasilnya.
  • Argumen .before dan .after mengatur posisi kolom baru, dan across() 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#