Teks yang baru diimpor jarang siap dianalisis. Catatan harian memuat spasi berlebih, kolom kode ditulis dengan gaya yang berbeda-beda, dan angka penting tersembunyi di tengah kalimat. Paket stringr menyediakan keluarga fungsi untuk pekerjaan semacam itu, dan topik ini membahas penggunaannya untuk mengukur, mendeteksi, mengambil, mengganti, memecah, serta membersihkan teks. Setiap fungsi diperkenalkan bertahap, mulai dari pencarian teks literal hingga pola regex, dengan vektor teks sederhana sebagai bahan contoh.
Konvensi keluarga fungsi str_#
stringr dapat digunakan langsung setelah paket tidyverse dimuat karena termasuk inti tidyverse. Seluruh fungsinya diawali awalan str_ sehingga mudah ditemukan melalui pelengkapan otomatis, dan argumen pertamanya selalu berupa vektor teks. Semua fungsi tervektorisasi, artinya satu panggilan memproses seluruh elemen vektor sekaligus tanpa perulangan eksplisit. Konsistensi ini membuat pola kerja antar fungsi serupa, yaitu vektor teks masuk dan vektor hasil keluar dengan panjang yang sama, sehingga hasilnya dapat langsung ditempelkan sebagai kolom baru. Vektor berikut memuat enam kalimat catatan harian yang digunakan sepanjang topik.
library(tidyverse)
catatan <- c(
"Kirim laporan mingguan sebelum pukul 09.00",
"Rapat koordinasi di ruang 204",
"Arsipkan berkas bulan Mei",
"Hubungi Ali di 0812-3456-7890",
"Perbarui kode buku BK-1023",
"Uji coba sistem baru"
)
catatan── 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
[1] "Kirim laporan mingguan sebelum pukul 09.00"
[2] "Rapat koordinasi di ruang 204"
[3] "Arsipkan berkas bulan Mei"
[4] "Hubungi Ali di 0812-3456-7890"
[5] "Perbarui kode buku BK-1023"
[6] "Uji coba sistem baru" Fungsi str_length() menghitung panjang tiap elemen dalam satuan karakter, termasuk spasi di antara kata. Fungsi str_to_upper(), str_to_lower(), dan str_to_title() mengubah bentuk hurufnya, masing-masing menjadi huruf besar seluruhnya, huruf kecil seluruhnya, dan huruf besar pada awal tiap kata.
str_length(catatan)[1] 42 29 25 29 26 20str_to_upper(catatan[3])
str_to_lower(catatan[1])
str_to_title("laporan mingguan")[1] "ARSIPKAN BERKAS BULAN MEI"
[1] "kirim laporan mingguan sebelum pukul 09.00"
[1] "Laporan Mingguan"Spasi berlebih di awal atau akhir teks sering lolos saat data diketik atau diimpor, dan spasi itu ikut terhitung oleh str_length(). Fungsi str_trim() menghapus spasi di kedua tepi teks secara bawaan, dan karakter tab di tepi turut dibersihkan.
teks_kotor <- c(" Rapat pukul 09.00 ", "\tKirim laporan\t")
str_trim(teks_kotor)[1] "Rapat pukul 09.00" "Kirim laporan" Mendeteksi teks dengan str_detect()#
Fungsi str_detect() menguji apakah suatu pola muncul di dalam tiap elemen teks dan mengembalikan vektor logical sepanjang masukannya. Pada pencarian literal, pola dicocokkan apa adanya sebagai rangkaian karakter. Pencocokannya peka terhadap besar kecilnya huruf, sehingga pola “laporan” dan “Laporan” memberikan keluaran yang berbeda.
str_detect(catatan, "laporan")
str_detect(catatan, "Laporan")[1] TRUE FALSE FALSE FALSE FALSE FALSE
[1] FALSE FALSE FALSE FALSE FALSE FALSEElemen pertama memuat kata laporan, sehingga pencarian huruf kecil menghasilkan TRUE hanya pada elemen itu, sedangkan pencarian dengan huruf besar tidak menemukan kecocokan apa pun. Pencarian literal juga dapat menargetkan frasa, bukan hanya satu kata, misalnya frasa “bulan Mei” pada catatan ketiga. Fungsi ini juga menerima pola regex, dan contoh penggunaannya bersambung pada bagian berikutnya. Apabila satu elemen bernilai NA, hasil deteksinya juga NA, sehingga baris yang bermasalah tetap terlihat. Vektor logical semacam ini biasanya diteruskan ke filter() pada dplyr untuk memilih baris yang memuat teks tertentu, atau dihitung dengan sum() untuk mengetahui berapa banyak elemen yang cocok.
Pola regex dasar: kelas karakter dan kuantifikator#
Pola pencarian pada fungsi stringr tidak berhenti pada teks literal. Pola tersebut juga dapat ditulis sebagai regex (regular expression), yaitu notasi untuk menggambarkan bentuk teks yang dicari. Cara membangunnya bertahap dimulai dari kelas karakter, misalnya [0-9] yang berarti satu digit apa pun dari 0 sampai 9. Bentuk singkat untuk kelas yang sama adalah \d.
str_detect(catatan, "[0-9]")
str_detect(catatan, "\\d{4}")[1] TRUE TRUE FALSE TRUE TRUE FALSE
[1] FALSE FALSE FALSE TRUE TRUE FALSEPola [0-9] menandai setiap catatan yang memuat angka, yaitu catatan pukul 09.00, ruang 204, nomor telepon, dan kode buku. Kuantifikator (quantifier) melengkapi kelas karakter dengan menyatakan berapa kali kelas itu boleh berulang. Pola \d{4} berarti tepat empat digit berturut-turut, sehingga hanya nomor telepon dan kode buku yang terdeteksi karena rangkaian angka lainnya lebih pendek. Bentuk + yang muncul pada bagian berikutnya berarti satu kali atau lebih.
Mengambil potongan dengan str_extract()#
Fungsi str_extract() mengembalikan potongan teks pertama yang cocok dengan pola, bukan sekadar tanda kecocokan. Apabila tidak ada kecocokan, hasilnya NA. Fungsi str_extract_all() mengembalikan seluruh kecocokan dalam sebuah list, dengan character(0) untuk elemen yang tidak memuat kecocokan sama sekali. Kedua fungsi tersebut menerima pola regex yang sama dengan str_detect(), sehingga pola yang sudah terbukti mendeteksi angka dapat langsung digunakan untuk mengambilnya. List hasil str_extract_all() diperiksa elemen demi elemen melalui [[ ]], dan unlist() menyatukannya kembali menjadi satu vektor.
str_extract(catatan, "\\d+")
str_extract_all(catatan, "\\d+")[1] "09" "204" NA "0812" "1023" NA
[[1]]
[1] "09" "00"
[[2]]
[1] "204"
[[3]]
character(0)
[[4]]
[1] "0812" "3456" "7890"
[[5]]
[1] "1023"
[[6]]
character(0)Pola \d+ berarti satu digit atau lebih secara berturut-turut. Pada elemen pertama, pencocokan berhenti di “09” karena titik bukan digit, lalu rangkaian “00” diambil sebagai kecocokan berikutnya oleh str_extract_all(). Nomor telepon terpecah menjadi tiga rangkaian karena tanda hubung memisahkannya. Perbedaan NA dan character(0) penting diperhatikan, yaitu NA berarti elemen tidak memuat kecocokan pada str_extract(), sedangkan character(0) berarti list tersebut memuat vektor kosong untuk elemen yang tidak berkecocokan.
Mengganti dan menghapus dengan str_replace()#
Fungsi str_replace() mengganti kecocokan pertama pada tiap elemen dengan teks pengganti, sedangkan str_replace_all() mengganti seluruh kecocokannya. Fungsi str_remove() dan str_remove_all() merupakan singkatan untuk penggantian dengan string kosong, sehingga pola yang cocok dihapus sepenuhnya. Pola penggantinya juga dapat ditulis sebagai regex, sehingga bagian teks yang berbentuk pola dapat diganti tanpa menuliskan teks lengkapnya. Hasilnya kembali berupa vektor teks dengan panjang yang sama dengan masukannya, sehingga kolom teks pada tibble dapat diperlakukan dengan cara yang sama melalui mutate().
str_replace(catatan, "pukul", "jam")
str_replace_all(catatan[4], "-", "")
str_remove(catatan[5], "BK-")[1] "Kirim laporan mingguan sebelum jam 09.00"
[2] "Rapat koordinasi di ruang 204"
[3] "Arsipkan berkas bulan Mei"
[4] "Hubungi Ali di 0812-3456-7890"
[5] "Perbarui kode buku BK-1023"
[6] "Uji coba sistem baru"
[1] "Hubungi Ali di 081234567890"
[1] "Perbarui kode buku 1023"Bentuk str_remove() juga lebih cepat dibaca saat kode ditinjau ulang karena maksud menghapusnya tersurat pada namanya, sedangkan bentuk str_replace() lebih fleksibel untuk penggantian dengan teks lain. Pemilihan keduanya tidak memengaruhi hasil, tetapi kejelasan maksud membantu perbaikan di kemudian hari.
Memecah teks dengan str_split()#
Fungsi str_split() memecah tiap elemen teks pada pola pemisah dan mengembalikan list berisi vektor potongan. Argumen simplify = TRUE menyatukan hasilnya menjadi matriks, sehingga lebih nyaman dilihat apabila banyak elemen dipotong sekaligus. Pemisahnya juga dapat berupa regex, misalnya pemecahan pada spasi berurutan dengan pola " +", meskipun teks contoh ini cukup dipecah pada satu spasi. Hasil list cocok untuk teks dengan jumlah potongan yang berbeda-beda, sedangkan bentuk matriksnya cocok untuk potongan yang teratur. Apabila satu elemen tidak memuat pemisahnya sama sekali, hasilnya berupa satu potongan yang berisi teks lengkapnya. Pemecahan menjadi beberapa kolom baru biasanya diteruskan dengan fungsi lain, sehingga pemisahan sederhana seperti contoh ini sudah mencakup kebutuhan yang paling sering muncul.
str_split(catatan[1], " ")
str_split(catatan[1], " ", simplify = TRUE)[[1]]
[1] "Kirim" "laporan" "mingguan" "sebelum" "pukul" "09.00"
[,1] [,2] [,3] [,4] [,5] [,6]
[1,] "Kirim" "laporan" "mingguan" "sebelum" "pukul" "09.00"Pemisahnya berupa spasi, dan hasil pertama berupa list dengan satu vektor per elemen masukan. Bentuk matriks pada keluaran kedua memudahkan pengambilan kolom tertentu, misalnya kata pertama dari setiap kalimat, karena semua elemen dipotong menjadi jumlah kolom yang sama.
Pembersihan data teks#
Fungsi-fungsi di atas paling sering digunakan bersama dalam mutate() dplyr untuk membersihkan satu kolom teks. Tibble berikut memuat nomor telepon dengan gaya penulisan yang berbeda-beda, lengkap dengan spasi tepi, tanda hubung, kurung, dan spasi di tengah.
kontak <- tibble(
nama = c("Ali", "Budi", "Citra", "Dewi"),
telepon = c(" 0812-3456-7890 ", "0813-9876-5432", "(021) 5550123", "0857 111 2233")
)
kontak <- kontak %>%
mutate(telepon_bersih = str_remove_all(str_trim(telepon), "[-() ]"))
kontak# A tibble: 4 × 3
nama telepon telepon_bersih
<chr> <chr> <chr>
1 Ali " 0812-3456-7890 " 081234567890
2 Budi "0813-9876-5432" 081398765432
3 Citra "(021) 5550123" 0215550123
4 Dewi "0857 111 2233" 08571112233 Kolom telepon_bersih lahir dari dua langkah pada satu ekspresi, yaitu str_trim() untuk menghapus spasi tepi, lalu str_remove_all() dengan pola [-() ] untuk menghapus tanda hubung, kurung, dan spasi. Di dalam kelas karakter itu, tanda hubung diletakkan paling awal agar dibaca sebagai karakter biasa dan bukan penanda rentang. Hasilnya berupa nomor yang seragam dan siap dibandingkan maupun disimpan. Nomor yang sudah bersih dapat diperiksa ulang dengan str_length(), karena nomor yang seragam biasanya berpanjang sama, sedangkan panjang yang menyimpang menunjukkan nomor yang belum tertangani. Apabila satu nomor bernilai NA, kedua fungsi pembersih meneruskannya apa adanya, sehingga baris yang bermasalah tetap terlihat pada kolom hasil.
Teknik yang sama berlaku untuk kode buku bergaya BK-1023. Bagian angkanya diambil dengan str_extract(), lalu diubah menjadi numerik setelah awalan BK- dihapus.
kode <- c("BK-1023", "BK-2044", "BK-3125")
str_extract(kode, "[0-9]+")
as.numeric(str_remove(kode, "BK-"))[1] "1023" "2044" "3125"
[1] 1023 2044 3125Ringkasan#
- Seluruh fungsi stringr berawalan
str_, argumen pertamanya selalu berupa vektor teks, dan semuanya tervektorisasi. str_length()menghitung panjang teks,str_to_upper()beserta keluarganya mengubah bentuk huruf, danstr_trim()menghapus spasi tepi.str_detect()menguji keberadaan pola dan menghasilkan vektorlogical, sedangkanstr_extract()danstr_extract_all()mengembalikan potongan yang cocok.- Pola regex dibangun bertahap dari teks literal, kelas karakter seperti
[0-9]atau\d, lalu kuantifikator seperti{4}dan+. str_replace()mengganti kecocokan pertama,str_replace_all()mengganti semuanya,str_remove_all()menghapus pola, danstr_split()memecah teks pada pola pemisah.
Referensi#
- stringr: Simple, Consistent Wrappers for Common String Operations — tidyverse
— dokumentasi resmi seluruh fungsi
str_beserta contoh pola regexnya. - R for Data Science, edisi ke-2 — Wickham, Çetinkaya-Rundel, Grolemund — bab regular expressions membahas pencarian dan pembersihan teks dengan stringr.