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.
library(tidyverse)
jadwal <- tribble(
~kode, ~mata_pelajaran,
"7A-2026", "Matematika",
"7B-2026", "Bahasa Indonesia",
"7A-2027", "Matematika"
)
jadwal── 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.
terpisah <- separate(jadwal, col = kode, into = c("kelas", "tahun"), sep = "-")
terpisah# 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.
separate_wider_delim(jadwal, cols = kode, delim = "-", names = c("kelas", "tahun"))# 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.
campur <- tribble(
~kode,
"7A-2026",
"7B"
)
separate_wider_delim(campur, cols = kode, delim = "-", names = c("kelas", "tahun"))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.
separate(campur, col = kode, into = c("kelas", "tahun"), sep = "-")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.
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# A tibble: 2 × 4
tahun bulan hari hadir
<chr> <chr> <chr> <dbl>
1 2026 06 01 28
2 2026 06 02 30Fungsi 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.
harian <- unite(harian, col = tanggal, tahun, bulan, hari, sep = "-")
harian
identical(harian, harian_asli)# A tibble: 2 × 2
tanggal hadir
<chr> <dbl>
1 2026-06-01 28
2 2026-06-02 30
[1] TRUEPemisah 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.
separate(jadwal, col = kode, sep = "-")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.
angka <- tibble(tahun = 2026, bulan = 6, hari = 1)
unite(angka, tanggal, tahun, bulan, hari, sep = "-")# A tibble: 1 × 1
tanggal
<chr>
1 2026-6-1Hasil 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 argumenintodansep, lalu menghapus kolom asalnya secara bawaan.separate()berstatus superseded, danseparate_wider_delim()menjadi penerusnya dengan perilaku yang lebih ketat pada data tak konsisten.separate()mengisi potongan yang kurang dengan NA disertai peringatan, sedangkanseparate_wider_delim()menghentikan eksekusi.unite()menggabungkan beberapa kolom menjadi satu dengan garis bawah sebagai pemisah bawaan.
Referensi#
- tidyr: Tidy Messy Data — tidyverse — dokumentasi resmi tidyr, termasuk halaman separate, separate_wider_delim, dan unite beserta seluruh argumennya.
- R for Data Science, edisi ke-2 — Wickham, Çetinkaya-Rundel, Grolemund — bagian Tidy data membahas pemisahan dan penggabungan kolom sebagai bagian pembentukan ulang tabel.