separate dan unite

Memisahkan satu kolom menjadi beberapa kolom dan menggabungkan beberapa kolom menjadi satu.

Daftar isi

Satu kolom kadang menyimpan beberapa variabel sekaligus, misalnya kode 7A-2026 yang merangkai kelas dan tahun, dan sebaliknya beberapa kolom kadang perlu disatukan menjadi satu. Topik ini membahas pemisahan kolom dengan separate() beserta penerusnya separate_wider_delim(), penggabungan kolom dengan unite(), serta peredaran bolak-balik pada contoh tanggal 2026-06-01 yang dipecah menjadi tiga kolom lalu digabung kembali. Kedua arah perubahan ini saling melengkapi karena sama-sama mengurus letak variabel, bukan isinya.

Memisahkan kolom dengan separate#

Kolom kode pada jadwal pelajaran berikut menyimpan dua variabel, yaitu kelas dan tahun ajaran, yang dirangkai dengan tanda hubung. Format serupa sering dipakai sebagai kunci gabungan pada berkas rekap sekolah. Pemecahannya menyederhanakan penyaringan per kelas maupun per tahun.

r Contoh: kolom kode yang merangkai dua variabel
library(tidyverse)
jadwal <- tribble(
  ~kode,     ~mata_pelajaran,
  "7A-2026", "Matematika",
  "7B-2026", "Bahasa Indonesia",
  "7A-2027", "Matematika"
)
jadwal
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
# A tibble: 3 × 2
  kode    mata_pelajaran  
  <chr>   <chr>           
1 7A-2026 Matematika      
2 7B-2026 Bahasa Indonesia
3 7A-2027 Matematika      

Fungsi separate() memecah kolom tersebut. Argumen col menyebut kolom sumber, argumen into memberi nama kolom-kolom hasil, dan argumen sep menentukan pemisahnya. Nama kolom hasil ditulis berurutan dari potongan paling kiri.

r Contoh: memecah kode menjadi kelas dan tahun
terpisah <- separate(jadwal, col = kode, into = c("kelas", "tahun"), sep = "-")
terpisah
text Keluaran
# A tibble: 3 × 3
  kelas tahun mata_pelajaran  
  <chr> <chr> <chr>           
1 7A    2026  Matematika      
2 7B    2026  Bahasa Indonesia
3 7A    2027  Matematika      

Pemisah pada sep dapat berupa karakter seperti tanda hubung di atas, maupun bilangan yang menyatakan posisi potongan, sehingga sep = 2 memotong setelah karakter kedua. Bentuk karakternya diartikan sebagai ekspresi reguler, sehingga satu pola dapat menampung beberapa bentuk pemisah sekaligus, misalnya sep = "[- ]" menerima tanda hubung maupun spasi. Kolom kode hilang dari hasil karena argumen remove secara bawaan bernilai TRUE, dan nilai itu dapat diubah menjadi FALSE apabila kolom asal masih diperlukan.

Hasil pemisahan bertipe character karena seluruhnya berasal dari potongan teks. Kolom tahun pada tabel di atas tampak seperti angka, tetapi belum dapat dihitung sampai dikonversi, misalnya lewat argumen convert = TRUE atau fungsi as.numeric(). Perilaku ini wajar karena separate() tidak dapat mengetahui maksud setiap potongan, sehingga keputusan konversinya diserahkan kepada pengguna.

separate_wider_delim sebagai penerus separate#

Sejak tidyr 1.3, keluarga separate_wider_*() menjadi penerus yang disarankan, sedangkan separate() berstatus superseded, yaitu masih stabil dan tidak akan dihapus, tetapi tidak lagi dikembangkan. Fungsi separate_wider_delim() memecah kolom berdasarkan pemisah, dan untuk data yang rapi hasilnya sama dengan separate(). Kedua status tersebut perlu diketahui agar pemilihan fungsi menjadi keputusan yang sadar, bukan sekadar kebiasaan.

r Contoh: pemecahan dengan separate_wider_delim
separate_wider_delim(jadwal, cols = kode, delim = "-", names = c("kelas", "tahun"))
text Keluaran
# A tibble: 3 × 3
  kelas tahun mata_pelajaran  
  <chr> <chr> <chr>           
1 7A    2026  Matematika      
2 7B    2026  Bahasa Indonesia
3 7A    2027  Matematika      

Hasilnya identik dengan pemanggilan separate(). Perbedaan utamanya terletak pada argumennya, yaitu kolom sumber disebut melalui cols, sedangkan nama-nama hasil diberikan lewat names alih-alih into. Keluarga ini juga menyediakan separate_wider_position() untuk pemotongan menurut posisi karakter dan separate_wider_regex() untuk pola ekspresi reguler, sehingga kasus yang tidak berbasis pemisah tetap tercakup. Keluarga fungsi baru ini menuntut aturan yang lebih ketat apabila datanya tidak seragam, sebagaimana diperlihatkan pada bagian berikut. separate() tetap aman dipakai karena mapan dan hasilnya sama, sedangkan kode baru sebaiknya mengarah ke penerusnya agar tahan lama.

Perilaku pada data yang tak konsisten#

Ketatnya separate_wider_delim() baru terasa apabila data tidak seragam. Pada tabel berikut, baris kedua tidak memiliki pemisah, sehingga tidak dapat menghasilkan dua kolom seperti baris pertama. Ketidakseragaman seperti ini sering diwarisi dari input manual.

r Contoh: separate_wider_delim berhenti pada data tak konsisten
campur <- tribble(
  ~kode,
  "7A-2026",
  "7B"
)
separate_wider_delim(campur, cols = kode, delim = "-", names = c("kelas", "tahun"))
text Keluaran
Error in separate_wider_delim(campur, cols = kode, delim = "-", names = c("kelas",      "tahun")) : Expected 2 pieces in each element of `kode`.
! 1 value was too short.
ℹ Use `too_few = "debug"` to diagnose the problem.
ℹ Use `too_few = "align_start"/"align_end"` to silence this message.

Nilai bawaan too_few = "error" menghentikan eksekusi apabila ada baris yang kekurangan potongan, dan aturan serupa berlaku untuk potongan berlebih melalui argumen too_many. Mode too_few = "debug" menandai baris yang bermasalah, sehingga sumber kekurangannya dapat ditelusuri terlebih dahulu. Perilaku yang ketat ini memang merepotkan, tetapi mencegah data yang hilang lolos tanpa diketahui, sehingga cocok untuk data yang dituntut sudah rapi sejak awal.

r Contoh: separate melanjutkan dengan pengisian NA
separate(campur, col = kode, into = c("kelas", "tahun"), sep = "-")
text Keluaran
Warning: Expected 2 pieces. Missing pieces filled with `NA` in 1 rows [2].
# A tibble: 2 × 2
  kelas tahun
  <chr> <chr>
1 7A    2026 
2 7B    <NA> 

Fungsi separate() menempuh jalan tengah, yaitu potongan yang kurang diisi NA disertai peringatan seperti pada baris 7B di atas, dan potongan berlebih dibuang juga disertai peringatan. Perbedaan perilaku ini yang perlu dipertimbangkan saat memilih fungsi, yaitu berhenti cepat dengan separate_wider_delim(), atau melanjutkan proses dengan pengisian NA melalui separate(). NA hasil pengisian tersebut kemudian dapat ditangani dengan filter() atau replace_na() sesuai kebutuhan analisis.

Memecah dan menggabungkan kembali tanggal#

Contoh penerapan yang lazim adalah kolom tanggal bertipe teks, misalnya 2026-06-01, yang perlu dipecah menjadi tahun, bulan, dan hari untuk pengelompokan per periode. Pemecahan juga memudahkan penyaringan per bulan tanpa mengurai teksnya setiap kali. Tabel kehadiran berikut memuat kolom tanggal semacam itu.

r Contoh: tanggal dipecah menjadi tiga kolom
harian_asli <- tribble(
  ~tanggal,     ~hadir,
  "2026-06-01", 28,
  "2026-06-02", 30
)
harian <- separate(harian_asli, col = tanggal, into = c("tahun", "bulan", "hari"), sep = "-")
harian
text Keluaran
# A tibble: 2 × 4
  tahun bulan hari  hadir
  <chr> <chr> <chr> <dbl>
1 2026  06    01       28
2 2026  06    02       30

Fungsi unite() mengembalikan susunan semula. Argumen pertama setelah data adalah nama kolom hasil, diikuti kolom-kolom yang digabungkan, dan argumen sep berisi pemisah antar nilainya. Urutan penyebutan kolom menentukan urutan potongan pada hasilnya.

r Contoh: tiga kolom digabung kembali menjadi tanggal
harian <- unite(harian, col = tanggal, tahun, bulan, hari, sep = "-")
harian
identical(harian, harian_asli)
text Keluaran
# A tibble: 2 × 2
  tanggal    hadir
  <chr>      <dbl>
1 2026-06-01    28
2 2026-06-02    30
[1] TRUE

Pemisah yang dipakai sama dengan pemisah semula, sehingga kolom tanggal kembali persis seperti data awal dan fungsi identical() mengonfirmasinya dengan TRUE. Apabila sep tidak diberikan, unite() memakai garis bawah sebagai pemisah bawaan. Kolom-kolom yang digabungkan juga hilang dari hasil karena remove secara bawaan bernilai TRUE, sama seperti pada separate(). Kombinasi kedua fungsi ini juga berguna untuk menyusun ulang urutan potongan, misalnya mengubah susunan hari-bulan-tahun menjadi tahun-bulan-tahun tanpa mengubah isinya.

Kesalahan umum#

Kesalahan pertama adalah memanggil separate() tanpa argumen into. Fungsi ini tidak dapat menebak nama kolom hasil, sehingga eksekusinya berhenti. Argumen ini mudah terlewat karena tidak memiliki nilai bawaan.

r Contoh: separate tanpa argumen into
separate(jadwal, col = kode, sep = "-")
text Keluaran
Error in separate(jadwal, col = kode, sep = "-") : `into` must be a character vector, not absent.

Pesan `into` must be a character vector menegaskan bahwa nama-nama hasil wajib disebut. Panjang vektor tersebut menentukan banyaknya kolom hasil, sehingga disusun sesuai urutan potongan dari kiri ke kanan. Apabila banyaknya potongan berbeda antarbaris, keputusan tentang potongan yang diisi NA atau dibuang perlu diambil terlebih dahulu.

Kesalahan kedua adalah menggabungkan kolom angka tanpa menyadari konversinya. Fungsi unite() mengubah setiap kolom menjadi teks terlebih dahulu, dan angka 6 menjadi teks 6, bukan 06. Kolom-kolom asal juga hilang dari hasil karena remove secara bawaan menghapusnya.

r Contoh: penggabungan kolom angka tanpa nol di depan
angka <- tibble(tahun = 2026, bulan = 6, hari = 1)
unite(angka, tanggal, tahun, bulan, hari, sep = "-")
text Keluaran
# A tibble: 1 × 1
  tanggal 
  <chr>   
1 2026-6-1

Hasil 2026-6-1 bukan lagi format tanggal yang sah. Apabila formatnya harus dipertahankan, formatkan kolomnya terlebih dahulu, misalnya dengan sprintf("%02d", bulan), atau simpan bulan sebagai teks 06 sejak awal agar penggabungan tidak mengubah bentuknya. Pilihan jalan keluarnya bergantung pada apakah kolom angka semula masih dipakai untuk penghitungan lain.

Ringkasan#

  • separate() memecah satu kolom menjadi beberapa kolom berdasarkan argumen into dan sep, lalu menghapus kolom asalnya secara bawaan.
  • separate() berstatus superseded, dan separate_wider_delim() menjadi penerusnya dengan perilaku yang lebih ketat pada data tak konsisten.
  • separate() mengisi potongan yang kurang dengan NA disertai peringatan, sedangkan separate_wider_delim() menghentikan eksekusi.
  • unite() menggabungkan beberapa kolom menjadi satu dengan garis bawah sebagai pemisah bawaan.

Referensi#