Berada di era digital yang serba terhubung, pengguna aplikasi seluler telah meledak dalam beberapa tahun terakhir. Beragam aplikasi hadir untuk memenuhi kebutuhan dan gaya hidup pengguna modern. Dalam konteks era yang semakin berkembang ini, terdapat satu aplikasi yang cukup menarik perhatian dengan konsep yang inovatif: KasirPintar. KasirPintar erupakan solusi kasir yang cerdas, KasirPintar hadir sebagai pilihan ideal bagi bisnis kecil dan menengah yang ingin mengelola penjualan dan inventaris mereka dengan lebih efisien. KasirPintar bukan sekadar aplikasi kasir biasa, tetapi juga membawa konsep yang baru dan membantu bisnis mengoptimalkan proses mereka. Dengan fitur-fitur cerdas dan intuitif, KasirPintar menyediakan pengalaman yang komprehensif dan memudahkan pengguna dalam mengelola transaksi penjualan, mengontrol stok barang, dan melacak performa bisnis dan sebagainya.
Dalam lingkungan bisnis yang penuh persaingan ini, penilaian pengguna terhadap aplikasi sangatlah penting dan perlu diperhatikan. Ulasan-ulasan pengguna menjadi sumber informasi berharga bagi pengembang dan calon pengguna, memperkuat reputasi dan keunggulan aplikasi ini di pasar. Namun, untuk memahami lebih dalam ulasan pengguna tersebut, dibutuhkan pendekatan yang canggih dan efisien. Dalam project ini, akan dilakukan pemodelan topik dengan metode Latent Direchlet Allocation (LDA) untuk menganalisis penilaian pengguna terhadap aplikasi KasirPintar. Melalui project ini juga kita bisa mengidentifikasi topik utama yang dibahas oleh pengguna, mengetahui sentimen yang terkait dengan setiap topik, dan memahami kebutuhan serta harapan pengguna terhadap KasirPintar.
Jumlah row data yang diambil untuk project ini adalah sebanyak 15.000 ulasan pengguna. Dengan menggunakan data ini, diharapkan dapat mengungkap wawasan dan pola yang bermanfaat mengenai penilaian pengguna terhadap aplikasi KasirPintar. Analisis yang dilakukan akan membantu dalam memahami kekuatan dan kelemahan aplikasi, serta memberikan kontribusi untuk meningkatkan kualitas dan pengalaman pengguna secara keseluruhan.
Business Impact yang diharapkan dari proses teks mining dan analisis yang dilakukan dalam project ini adalah dapat membantu pelaku bisnis (khususnya dalam hal ini PT Kasir Pintar Internasional) dalam mengelola keputusan bisnis dan melakukan product development yang didukung dengan informasi :
Sebagai langkah pertama, mari kita mendefisikan data yang akan digunakan dalam project ini .
data <- read.csv("kasirpintar15k.csv")
data# Menambahkan kolom baru yang menyimpan review asli
data$content_real <- data$contentHasil crawling yang telah dilakukan menghasilkan banyak kolom/variabel, seperti reviewId, userName, userImage dan sebagainya. Beberapa kolom tampaknya tidak berkepentingan dalam proses analisis yang akan dilakukan. Sehingga kita hanya perlu menyeleksi kolom-kolom yang kita butuhkan, dalam hal ini kita mengambil kolom content (review), score (rating), at (waktu rating diberikan), dan kolom appVersion.
library(dplyr)
data <- data %>% select(c(content, content_real, score, at, appVersion))
glimpse(data)#> Rows: 15,000
#> Columns: 5
#> $ content <chr> "Mudah digunakan dan simple", "Sangat membantu sekali", "…
#> $ content_real <chr> "Mudah digunakan dan simple", "Sangat membantu sekali", "…
#> $ score <int> 5, 5, 5, 5, 4, 5, 4, 5, 5, 1, 5, 5, 5, 4, 5, 5, 5, 5, 5, …
#> $ at <chr> "2023-06-21 22:35:34", "2023-06-21 17:48:50", "2023-06-21…
#> $ appVersion <chr> "4.9.8", "4.9.8", "4.9.8", "4.9.8", "4.9.8", "4.9.8", "4.…
Sebelum melakukan proses analisis dan permodelan, tentu terdapat beberapa penyesuaian yang perlu dilakukan terhadap data. Proses ini mencakup penyesuaian tipe data, ekstraksi data, dan pembersihan data teks review.
Kolom at yang menunjukkan informasi waktu pengguna memberikan review. Kolom ini akan diekstraksi ke dalam kolom-kolom baru. Disini kita akan mengambil informasi tanggal-bulan-tahun, informasi hari, dan informasi jam berapa pengguna memberikan review tersebut. Nantinya ini akan membantu kita dalam melihat pola-pola pengguna dalam memberikan review dalam konteks waktu.
library(lubridate)
data$at <- ymd_hms(data$at)
data$review_date <- date(data$at)
data$review_day <- wday(data$at, label = T, abbr = F)
data$review_hour <- hour(data$at)
glimpse(data)#> Rows: 15,000
#> Columns: 8
#> $ content <chr> "Mudah digunakan dan simple", "Sangat membantu sekali", "…
#> $ content_real <chr> "Mudah digunakan dan simple", "Sangat membantu sekali", "…
#> $ score <int> 5, 5, 5, 5, 4, 5, 4, 5, 5, 1, 5, 5, 5, 4, 5, 5, 5, 5, 5, …
#> $ at <dttm> 2023-06-21 22:35:34, 2023-06-21 17:48:50, 2023-06-21 15:…
#> $ appVersion <chr> "4.9.8", "4.9.8", "4.9.8", "4.9.8", "4.9.8", "4.9.8", "4.…
#> $ review_date <date> 2023-06-21, 2023-06-21, 2023-06-21, 2023-06-21, 2023-06-…
#> $ review_day <ord> Wednesday, Wednesday, Wednesday, Wednesday, Wednesday, We…
#> $ review_hour <int> 22, 17, 15, 14, 10, 8, 5, 2, 21, 18, 15, 9, 23, 18, 20, 9…
Data text ulasan yang kita punya belum sepenuhnya layak untuk dimodelkan. Dalam data tersebut masih terdapat cukup banyak kesalahan kata (typo), emoticon, simbol-simbol yang nantinya berdampak pada hasil yang akan diperoleh (hasil pemodelannya menjadi kurang baik). Untuk itulah kita perlu melakukan threatment terhadap data text yang ada, diharapkan dengan melakukan ini kita bisa memaksimalkan hasil analisis nantinya.
Code dibawah ini akan dijalankan untuk menghapus emoticon, menghapus hyperlink, tanda baca, angka, dan menjadikan text dalam bentuk huruf kecil.
library(stringi)
library(magrittr)
data$content <- data$content %>%
stri_replace_all_regex("\\p{So}+", "") %>% # menghapus emoticon
gsub("http\\S+|www\\S+", "", .) %>% # menghapus hyperlink
gsub("[[:punct:]]", "", .) %>% # menghapus tanda baca
gsub("[0-9]+", "", .) %>% # menghapus angka
tolower() # convert ke huruf kecil
head(data$content, 10)#> [1] "mudah digunakan dan simple"
#> [2] "sangat membantu sekali"
#> [3] "saya senang dengan aplikasi ini saya membatu usaha kecil saya berjualan buah"
#> [4] "sangat membantu"
#> [5] "baik"
#> [6] "sangat membantu"
#> [7] "baru mencoba yang geratis sejauh ini bagus dan mudah serta murah sangat membantu dalam mengelola barang dagangan"
#> [8] "untuk saat ini enak dipakai"
#> [9] "teeimakasih sangat membantu utk mengetahui profit margin jualan"
#> [10] "akun dinonaktifkan peeh"
Bisa dilihat pada preview data diatas, masih terdapat beberapa kesalahan kata (typo). Seperti “utk” yang seharusnya adalah “untuk”, “teeimakasih” yang seharusnya “terimakasih”, dan kesalahan lainnya. Dalam analisis teks atau pemodelan bahasa, penting untuk mencapai konsistensi dan standar dalam representasi kata-kata. Typo ataupun slang seringkali merupakan variasi atau bentuk tidak resmi dari kata-kata yang digunakan dalam percakapan sehari-hari. Dengan mengganti slang dengan bentuk standar, kita dapat memastikan konsistensi dalam data dan mempermudah pemrosesan teks. Untuk mengatasi kesalahan-kesalahan kata yang ada, kita akan menggantinya kedalam bentuk baku/formal dengan menggunakan kamus slank yang dalam hal ini telah dipersiapkan sebelumnya.
library(future)
plan(multisession, workers = 4)
library(jsonlite)
# Read data kamus combined_slang_words.txt
txt <- readLines("combined_slang_words.txt", warn = FALSE)
# Combine the lines into a single string
json_string <- paste(txt, collapse = "")
# Convert the JSON string to a data frame
slang.dict <- jsonlite::fromJSON(json_string, simplifyDataFrame = FALSE)
# Convert the data to a data frame
slang.dict <- data.frame(slang = names(slang.dict), formal = unlist(slang.dict), stringsAsFactors = FALSE)
# Menghapus kemungkinan duplikasi pada kamus slang
slang.dict <- slang.dict[!duplicated(slang.dict$slang), ]
replace_slang <- function(text, slang_dict) {
for (i in seq_along(slang_dict$slang)) {
pattern <- paste0("\\b", slang_dict$slang[i], "\\b")
replacement <- slang_dict$formal[i]
text <- gsub(pattern, replacement, text, ignore.case = TRUE)
}
return(text)
}
data$content <- replace_slang(data$content, slang.dict)
# Menampilkan hasil
head(data$content)#> [1] "mudah digunakan dan simple"
#> [2] "sangat membantu sekali"
#> [3] "saya senang dengan aplikasi ini saya membatu usaha kecil saya berjualan buah"
#> [4] "sangat membantu"
#> [5] "baik"
#> [6] "sangat membantu"
head(data$content, 15)#> [1] "mudah digunakan dan simple"
#> [2] "sangat membantu sekali"
#> [3] "saya senang dengan aplikasi ini saya membatu usaha kecil saya berjualan buah"
#> [4] "sangat membantu"
#> [5] "baik"
#> [6] "sangat membantu"
#> [7] "baru mencoba yang geratis sejauh ini bagus dan mudah serta murah sangat membantu dalam mengelola barang dagangan"
#> [8] "untuk saat ini enak dipakai"
#> [9] "teeimakasih sangat membantu untuk mengetahui profit margin jualan"
#> [10] "akun dinonaktifkan peeh"
#> [11] "good lah"
#> [12] "bagus membantu"
#> [13] "sangat membantu "
#> [14] "sangat membantu"
#> [15] "aplikasi kasir terbaik"
Kamus combined_slang_words.txt belum sepenuhnya mampu memperbaiki semua kesalahan yang ada. Masih tampak kesalahan kata lainnya seperti “simple” yang seharusnya dalam bahasa indonesia “simpel”, “geratis” yang seharusnya “gratis”, dan sebagainya. Untuk memperbaikinya, kita akan menggunakan kamus slang tambahan. Kamus slang yang digunakan kali ini memuat 16.000 lebih kata non-baku yang disertai dengan bentuk bakunya.
# Load library yang dibutuhkan
library(stringr)
# Definisikan fungsi untuk menggantikan kata slang
ganti_slang <- function(teks, kamus_slang) {
for (i in seq_along(kamus_slang$slang)) {
pola <- paste0("\\b(?i)", kamus_slang$slang[i], "\\b")
teks <- str_replace_all(teks, pola, kamus_slang$formal[i])
}
return(teks)
}
# Baca kamus slang
kamus_slang <- read.csv("kamus_slang.csv", stringsAsFactors = FALSE)
kamus_slang <- kamus_slang[!duplicated(kamus_slang$slang),]
# Terapkan penggantian slang pada kolom content
data$content <- ganti_slang(data$content, kamus_slang)
# Tampilkan isi kolom content yang sudah diperbarui
head(data$content)#> [1] "mudah digunakan dan simple"
#> [2] "sangat membantu sekali"
#> [3] "saya senang dengan aplikasi ini saya membantu usaha kecil saya berjualan buah"
#> [4] "sangat membantu"
#> [5] "baik"
#> [6] "sangat membantu"
head(data$content, 15)#> [1] "mudah digunakan dan simple"
#> [2] "sangat membantu sekali"
#> [3] "saya senang dengan aplikasi ini saya membantu usaha kecil saya berjualan buah"
#> [4] "sangat membantu"
#> [5] "baik"
#> [6] "sangat membantu"
#> [7] "baru mencoba yang gratis sejauh ini bagus dan mudah serta murah sangat membantu dalam mengelola barang dagangan"
#> [8] "untuk saat ini enak dipakai"
#> [9] " terimakasih sangat membantu untuk mengetahui profit margin jualan"
#> [10] "akun dinonaktifkan peeh"
#> [11] "bagus lah"
#> [12] "bagus membantu"
#> [13] "sangat membantu "
#> [14] "sangat membantu"
#> [15] "aplikasi kasir terbaik"
Stemming adalah proses mengubah kata-kata menjadi bentuk dasarnya (atau kata dasar). Tujuan utama stemming adalah untuk mengurangi variasi morfologi dalam teks, sehingga kata-kata dengan akhiran atau awalan yang berbeda tetapi memiliki akar kata yang sama dapat diperlakukan sebagai satu entitas. Dengan melakukan stemming, kita dapat mengurangi jumlah bentuk kata yang berbeda dalam analisis teks. Dalam analisis teks, adanya variasi bentuk kata yang berbeda-beda dapat menghasilkan banyak variasi yang sebenarnya memiliki akar kata yang sama. Dengan melakukan stemming, variasi bentuk kata tersebut dapat direduksi menjadi bentuk dasarnya, sehingga mengurangi dimensi atau jumlah unik kata dalam data. Ini membantu dalam mengurangi kompleksitas dan meningkatkan efisiensi pemrosesan data teks.
# Stemming : mengubah bentuk kata menjadi kata dasar
# Load library yang dibutuhkan
library(quanteda)
# Definisikan fungsi untuk stemming
stemming_id <- function(teks) {
teks_stem <- tokens(teks) %>%
tokens_wordstem(language = "id")
return(teks_stem)
}
# Terapkan stemming pada kolom content
data$content <- stemming_id(data$content)
# Tampilkan isi kolom content yang sudah diterapkan stemming
head(data$content)#> Tokens consisting of 6 documents.
#> text1 :
#> [1] "mudah" "guna" "dan" "simple"
#>
#> text2 :
#> [1] "sangat" "bantu" "sekal"
#>
#> text3 :
#> [1] "saya" "senang" "dengan" "aplikasi" "ini" "saya"
#> [7] "bantu" "usaha" "kecil" "saya" "jual" "buah"
#>
#> text4 :
#> [1] "sangat" "bantu"
#>
#> text5 :
#> [1] "baik"
#>
#> text6 :
#> [1] "sangat" "bantu"
Pada tahap ini kita akan menghapus kata-kata yang tidak memiliki makna/tidak terlalu esensial terhadap analisis kita, Sebagai contoh: yang, dengan, untuk, kepada dan lainnya. Dengan menghapus stopwords, kita dapat menghilangkan noise atau gangguan yang mungkin muncul dalam analisis teks. Ini membantu dalam memfokuskan pada kata-kata yang lebih penting dan relevan dalam analisis yang akan dilakukan.
# StopWord : Proses menghapus kata-kata yang tidak terlalu bermakna
# Baca file stopword
stopwords <- readLines("combined_stop_words.txt", warn = FALSE, encoding = "UTF-8")
# Definisikan fungsi untuk menghapus stopword
remove_stopwords <- function(teks, stopwords) {
teks_clean <- sapply(teks, function(x) {
words <- unlist(strsplit(x, "\\s+"))
words <- words[!(tolower(words) %in% stopwords)]
paste(words, collapse = " ")
})
return(teks_clean)
}
# Terapkan penghapusan stopword pada kolom content
data$content <- remove_stopwords(data$content, stopwords)
# Tampilkan isi kolom content setelah penghapusan stopword
head(data$content, 20)#> [1] "mudah simple"
#> [2] "bantu sekal"
#> [3] "senang aplikasi bantu usaha kecil jual buah"
#> [4] "bantu"
#> [5] ""
#> [6] "bantu"
#> [7] "coba gratis bagus mudah murah bantu dalam elola barang dagang"
#> [8] "enak paka"
#> [9] "imakasih bantu etahu profit margin jual"
#> [10] "akun nonaktif peeh"
#> [11] "bagus"
#> [12] "bagus bantu"
#> [13] "bantu"
#> [14] "bantu"
#> [15] "aplikasi kasir"
#> [16] "semoga gratis"
#> [17] "ajar jual"
#> [18] "apik ojek online aneh"
#> [19] "aplikasi kasir paka mudah praktis"
#> [20] "keren"
Pada data teks kita masih dijumpai beberapa unsur yang tidak terlalu bermakna, seperti “nya”, “lah”, unsur “-nya” (contoh “aplikasinya”). Oleh karena unsur-unsur ini tidak memberikan informasi penting atau relevan dalam pemahaman konten teks, kita dapat mempertimbangkan untuk menghapusnya.
# Menghilangkan kata "nya"
data$content <- gsub("\\bnya\\b", "", data$content)
# Menghapus unsur "-nya" dari kolom "content"
data$content <- gsub("-nya\\b", "", data$content)
# Menghilangkan kata "lah"
data$content <- gsub("\\blah\\b", "", data$content)sapply(strsplit(data$content, " "), length) %>% summary()#> Min. 1st Qu. Median Mean 3rd Qu. Max.
#> 0.00 1.00 2.00 3.17 4.00 48.00
Kode diatas digunakan untuk menghitung jumlah kata dalam setiap elemen teks pada kolom “content” dalam objek “data$content”. Berdasarkan pengecekan jumlah kata diatas bisa dilihat bahwa terdapat elemen teks dengan jumlah katanya 0 (empty string). Kita akan menghapus row pada data yang tidak memiliki isi teks review agar memaksimalkan hasil dari model kita.
data <- data[!data$content == "", ]
dim(data)#> [1] 14669 8
Awalnya kita memiliki 15.000 row. Setelah dilakukan pembersihan sejauh ini, tersisa 14.669 row data ulasan.
sapply(strsplit(data$content, " "), length) %>% summary()#> Min. 1st Qu. Median Mean 3rd Qu. Max.
#> 1.000 1.000 2.000 3.242 4.000 48.000
data[91,]Sejauh ini data teks yang kita miliki masih belum bersih sepenuhnya, masih terdapat emoticon, kesalahan-kesalahan kata lainnya yang tidak bisa di-handle oleh proses sebelumnya. Maka dari itu kita akan kembali melakukan pembersihan secara lebih detail demi kualitas hasil analisis nantinya.
# Menghapus emotikon yang masih tampak pada data$content
data$content <- gsub("[\\x{1F600}-\\x{1F64F}\\x{1F300}-\\x{1F5FF}\\x{1F680}-\\x{1F6FF}\\x{2600}-\\x{26FF}\\x{2700}-\\x{27BF}\\x{1F900}-\\x{1F9FF}\\x{1F1E0}-\\x{1F1FF}\\x{1F191}-\\x{1F251}\\x{1F004}\\x{1F0CF}\\x{1F170}-\\x{1F171}\\x{1F17E}-\\x{1F17F}\\x{1F18E}\\x{3030}\\x{2B50}\\x{2B55}\\x{2E80}-\\x{2E99}\\x{2E9B}-\\x{2EF3}\\x{2F00}-\\x{2FD5}\\x{2FF0}-\\x{2FFB}\\x{E000}-\\x{F8FF}\\x{FE00}-\\x{FE0F}\\x{FE30}-\\x{FE4F}\\x{1F000}-\\x{1F02F}\\x{1F0A0}-\\x{1F0FF}\\x{1F100}-\\x{1F64F}\\x{1F680}-\\x{1F6FF}\\x{1F700}-\\x{1F773}\\x{1F780}-\\x{1F7D8}\\x{1F7E0}-\\x{1F7EB}\\x{1F800}-\\x{1F97F}\\x{1F9A0}-\\x{1F9E6}\\x{1F9F0}-\\x{1F9FF}]", "", data$content, perl = TRUE)data[91,]# Menghitung jumlah kata keseluruhan
# Menggabungkan seluruh teks menjadi satu string
all_text <- paste(data$content, collapse = " ")
# Menghitung jumlah kata
word_count <- length(strsplit(all_text, "\\s+")[[1]])
# Menampilkan jumlah kata
print(word_count)#> [1] 47031
Proses selanjutnya adalah kita akan menghapus kata-kata dengan frekuensi kemunculan yang <5. Kata-kata dengan frekuensi kemunculan kurang dari 5 tidak memberikan kontribusi signifikan atau relevan dalam analisis yang akan dilakukan dan penghapusan tersebut dapat membantu menyederhanakan data, mengurangi keberisian atau kekacauan dalam data teks.
# Menghapus kata-kata yang frekuensi kemunculannya sangat minim
# Menghitung frekuensi kata
word_freq <- table(unlist(strsplit(data$content, "\\s+")))
# Memfilter kata dengan frekuensi < 5
word_freq_filtered <- word_freq[word_freq < 5]
# Menampilkan kata dan frekuensi
length(word_freq_filtered)#> [1] 4290
Dari hasil running code diatas, kita bisa melihat bahwa terdapat 4.290 kata dengan frekuensi kemunculan kurang dari 5. Kata-kata tersebut akan kita filter untuk tidak akan digunakan dalam proses berikutnya.
# Menghapus kata dengan frekuensi < 5
data$content <- sapply(strsplit(data$content, "\\s+"), function(x) paste(x[word_freq[x] >= 5], collapse = " "))sapply(strsplit(data$content, " "), length) %>% summary()#> Min. 1st Qu. Median Mean 3rd Qu. Max.
#> 0.00 1.00 2.00 2.82 3.00 45.00
data <- data[!data$content == "", ]dim(data)#> [1] 14090 8
head(data$content, 20)#> [1] "mudah simple"
#> [2] "bantu sekal"
#> [3] "senang aplikasi bantu usaha kecil jual"
#> [4] "bantu"
#> [5] "bantu"
#> [6] "coba gratis bagus mudah murah bantu dalam elola barang dagang"
#> [7] "enak paka"
#> [8] "imakasih bantu etahu profit jual"
#> [9] "akun nonaktif"
#> [10] "bagus"
#> [11] "bagus bantu"
#> [12] "bantu"
#> [13] "bantu"
#> [14] "aplikasi kasir"
#> [15] "semoga gratis"
#> [16] "ajar jual"
#> [17] "apik online aneh"
#> [18] "aplikasi kasir paka mudah praktis"
#> [19] "keren"
#> [20] "bagus"
Kita akan melakukan validasi dan menjaga kualitas serta keakuratan 1000 kata dengan frekuensi kemunculan terbanyak. Hal ini dilakukan untuk menjamin bahwa kata-kata yang termasuk dalam top 1000 kata paling umum benar-benar mewakili kekayaan informasi dari data yang akan dianalisis.
# Menghitung frekuensi kata
word_freq <- table(unlist(strsplit(data$content, "\\s+")))
# Mengurutkan frekuensi kata secara menurun
top_1000_words <- names(head(sort(word_freq, decreasing = TRUE), 1000))
# Subset dataframe hanya dengan kata-kata paling sering muncul
data_top_1000 <- data[data$content %in% top_1000_words, ]
print(top_1000_words[1:40])#> [1] "bantu" "bagus" "aplikasi" "mantap" "usaha"
#> [6] "oke" "imakasih" "mudah" "sekal" "kasir"
#> [11] "coba" "banget" "pintar" "iya" "dalam"
#> [16] "paka" "keren" "transaksi" "jual" "gratis"
#> [21] "semoga" "kecil" "barang" "manfaat" "bintang"
#> [26] "kasih" "profesional" "lumay" "data" "struk"
#> [31] "bayar" "fitur" "suka" "toko" "top"
#> [36] "stok" "napa" "engah" "harga" "lapor"
Pada top1000 kata-kata diatas, masih terdapat beberapa kesalahan dalam katanya, seperti kata “imakasih” yang seharusnya “terimakasih”, “baguus” yang seharusnya “bagus”, dsb. Ditambah lagi, masih terdapat kata-kata yang tidak memiliki makna yang berarti, seperti “a”, “mm”, “pe”, dsb serta beberapa emoticon masih muncul. Oleh karena itu, kita akan kembali mengganti kesalahan yang ada dan menghapus kata/emoticon yang tidak bermakna tersebut supaya ketika kita masuk ke dalam proses pemodelan, data yang kita gunakan sudah cukup bersih.
# Membuat mapping kesalahan kata
typo_mapping <- c(
"paka" = "pakai",
"imakasih" = "terimakasih",
"sekal" = "sekali",
"lumay" = "lumayan",
"napa" = "kenapa",
"app" = "aplikasi",
"pudah" = "mudah",
"alhamdulil" = "alhamdulillah",
"sendir" = "sendiri",
"simple" = "simpel",
"jangan-jang" = "jangan-jangan",
"josss" = "jos",
"eluar" = "keluar",
"mengert" = "mengerti",
"goood" = "bagus",
"bagussangat" = "sangat bagus",
"cewa" = "kecewa",
"aplikasinya" = "aplikasi",
"irim" = "kirim",
"caya" = "percaya",
"god" = "good",
"matap" = "mantap",
"imkasih" = "terimakasih",
"ecewa" = "kecewa",
"goog" = "good",
"okey" = "oke",
"mhon" = "mohon",
"rama" = "ramah",
"rekomended" = "rekomendasi",
"click" = "klik",
"uninstal" = "uninstall",
"suwun" = "terimakasih",
"hrga" = "harga",
"joz" = "jos",
"ofline" = "offline",
"jaman" = "zaman",
"mantullll" = "mantap",
"mantullllll" = "mantap",
"trnsaksi" = "transaksi",
"bagusd" = "bagus",
"bantusekal" = "bantu sekali",
"apliaksi" = "aplikasi",
"erima" = "terima",
"ferivikasi" = "verifikasi",
"bagua" = "bagus",
"of" = "off",
"irim" = "kirim",
"baguus" = "bagus",
"membantu" = "bantu",
"nice" = "bagus",
"mantaab" = "mantap",
"gunakanaplikasi" = "gunakan aplikasi",
"business" = "bisnis",
"mbantu" = "bantu",
"mantaps" = "mantap",
"photo" = "foto",
"pintarsemoga" ="pintar semoga",
"seep" = "sip",
"qasir" = "kasir",
"mantull" = "mantap",
"karyaw" = "karyawan",
"ttrimakasih" = "terimakasih",
"sesua" = "sesuai",
"🎧" = "",
"🎴" = "",
"🌩" = "",
"okk" = "oke",
"proffil" = "profi;",
"mbantu" = "bantu",
"ramahh" = "ramah",
"bantua" = "bantu",
"bantutertterimakasih" = "bantu terimakasih",
"bantudan" = "bantu",
"bantuap" = "bantu",
"bantutrims" = "bantu terimakasih",
"baguse" = "bagus",
"bantumantap" = "bantu mantap",
"bantuuntuk" = "bantu untuk",
"ajiib" = "bagus",
"alhamdulillahlahsangat" = "alhamdulillah sangat",
"elola" = "kelola"
)
# Fungsi untuk mengganti kata-kata typo
replace_typo <- function(text) {
for (typo in names(typo_mapping)) {
text <- gsub(typo, typo_mapping[typo], text)
}
text
}
# Mengganti kata-kata typo dalam kolom "content"
data$content <- sapply(data$content, replace_typo)data$content[1:20]#> [1] "mudah simpel"
#> [2] "bantu sekali"
#> [3] "senang aplikasi bantu usaha kecil jual"
#> [4] "bantu"
#> [5] "bantu"
#> [6] "coba gratis bagus mudah murah bantu dalam kelola barang dagang"
#> [7] "enak pakai"
#> [8] "tterimakasih bantu etahu proffit jual"
#> [9] "akun nonaktif"
#> [10] "bagus"
#> [11] "bagus bantu"
#> [12] "bantu"
#> [13] "bantu"
#> [14] "aplikasi kasir"
#> [15] "semoga gratis"
#> [16] "ajar jual"
#> [17] "apik online aneh"
#> [18] "aplikasi kasir pakai mudah praktis"
#> [19] "keren"
#> [20] "bagus"
# Melakukan pengecekan kembali frekuensi kata
word_freq <- table(unlist(strsplit(data$content, "\\s+")))
# Mengurutkan frekuensi kata secara menurun
top_1000_words <- names(head(sort(word_freq, decreasing = TRUE), 1000))
# Subset dataframe hanya dengan kata-kata paling sering muncul
data_top_1000 <- data[data$content %in% top_1000_words, ]
print(top_1000_words[1:50])#> [1] "bantu" "bagus" "aplikasi" "mantap"
#> [5] "usaha" "oke" "tterimakasih" "mudah"
#> [9] "sekali" "kasir" "coba" "banget"
#> [13] "pintar" "iya" "dalam" "pakai"
#> [17] "keren" "transaksi" "jual" "gratis"
#> [21] "semoga" "kecil" "barang" "manfaat"
#> [25] "bintang" "kasih" "proffesional" "lumayan"
#> [29] "data" "struk" "bayar" "fitur"
#> [33] "simpel" "suka" "toko" "top"
#> [37] "stok" "kenapa" "engah" "harga"
#> [41] "lapor" "cetak" "admin" "hilang"
#> [45] "alhamdulillah" "sepert" "ajar" "ok"
#> [49] "deh" "sukses"
Pada kata dengan frekuensi kemunculan tinggi, kesalahan kata dan kata tidak bermakna masih ditemukan. Contohnya kata “tterimakasih”, “proffesional”, “iya”, “deh”, dan lain sebagainya. Kita akan melakukan konversi kata dan menghapus kata yang tidak bermakna tersebut secara manual.
# Replace kata secara manual
data$content <- gsub("tterimakasih", "terimakasih", data$content)
data$content <- gsub("proffesional", "profesional", data$content)
data$content <- gsub("ngitung", "hitung", data$content)
data$content <- gsub("ontrol", "kontrol", data$content)
data$content <- gsub("uasa", "kuasa", data$content)
data$content <- gsub("mbalik", "balik", data$content)
data$content <- gsub("baguss", "bagus", data$content)
data$content <- gsub("bagusap", "bagus", data$content)
data$content <- gsub("sayatertterimakasih", "terimakasih", data$content)
data$content <- gsub("sayasemoga", "semoga", data$content)
data$content <- gsub("pake", "pakai", data$content)
data$content <- gsub("profi;", "profil", data$content)
data$content <- gsub("ntaps", "mantap", data$content)
data$content <- gsub("topp", "top", data$content)
data$content <- gsub("prin", "print", data$content)
data$content <- gsub("thnks", "terimakasih", data$content)
data$content <- gsub("trx", "transaksi", data$content)
data$content <- gsub("mamtap", "mantap", data$content)
data$content <- gsub("free", "gratis", data$content)
data$content <- gsub("biasasangat", "biasa", data$content)
data$content <- gsub("uninstalll", "uninstall", data$content)
data$content <- gsub("usha", "usaha", data$content)
data$content <- gsub("perpercaya", "percaya", data$content)
data$content <- gsub("sangaat", "sangat", data$content)
data$content <- gsub("bantutrtterimakasih", "bantu terimakasih", data$content)
data$content <- gsub("aplikasilication", "aplikasi", data$content)
data$content <- gsub("aplication", "aplikasi", data$content)
data$content <- gsub("printt", "print", data$content)
data$content <- gsub("thankyou", "terimakasih", data$content)
data$content <- gsub("pakaii", "pakai", data$content)
data$content <- gsub("kkecewa", "kecewa", data$content)
data$content <- gsub("proffit", "profit", data$content)
data$content <- gsub("kkirim", "kirim", data$content)
data$content <- gsub("mantaplikasippp", "mantap", data$content)
data$content <- gsub("masu", "masuk", data$content)
data$content <- gsub("ok", "oke", data$content)
data$content <- gsub("whatsaplikasi", "whatsapp", data$content)
data$content <- gsub("mantapl", "mantap", data$content)
data$content <- gsub("kkontrol", "kontrol", data$content)
data$content <- gsub("membntu", "bantu", data$content)
data$content <- gsub("apliksi", "aplikasi", data$content)
data$content <- gsub("aplikasix", "aplikasi", data$content)
data$content <- gsub("bantutterima", "bantu", data$content)
data$content <- gsub("histor", "history", data$content)
data$content <- gsub("gratise", "gratis", data$content)
data$content <- gsub("mantaaaap", "mantap", data$content)
data$content <- gsub("baguuusss", "bagus", data$content)
data$content <- gsub("stoke", "stok", data$content)
data$content <- gsub("proffit", "profit", data$content)
data$content <- gsub("rekomend", "recommended", data$content)
data$content <- gsub("muantap", "mantap", data$content)
data$content <- gsub("okee", "oke", data$content)
data$content <- gsub("masukk", "masuk", data$content)
data$content <- gsub("sampa", "sampah", data$content)# Menghitung frekuensi kata
word_freq <- table(unlist(strsplit(data$content, "\\s+")))
# Mengurutkan frekuensi kata secara menurun
top_1000_words <- names(head(sort(word_freq, decreasing = TRUE), 1000))
# Subset dataframe hanya dengan kata-kata paling sering muncul
data_top_1000 <- data[data$content %in% top_1000_words, ]
print(top_1000_words[1:50])#> [1] "bantu" "bagus" "aplikasi" "mantap"
#> [5] "oke" "usaha" "terimakasih" "mudah"
#> [9] "sekali" "kasir" "coba" "banget"
#> [13] "pintar" "iya" "dalam" "pakai"
#> [17] "keren" "transaksi" "jual" "gratis"
#> [21] "semoga" "kecil" "barang" "manfaat"
#> [25] "bintang" "kasih" "profesional" "lumayan"
#> [29] "data" "struk" "bayar" "fitur"
#> [33] "simpel" "suka" "tokeo" "top"
#> [37] "stok" "kenapa" "masuk" "engah"
#> [41] "harga" "lapor" "cetak" "admin"
#> [45] "hilang" "alhamdulillah" "sepert" "ajar"
#> [49] "deh" "sukses"
data$content[1:20 ]#> [1] "mudah simpel"
#> [2] "bantu sekali"
#> [3] "senang aplikasi bantu usaha kecil jual"
#> [4] "bantu"
#> [5] "bantu"
#> [6] "coba gratis bagus mudah murah bantu dalam kelola barang dagang"
#> [7] "enak pakai"
#> [8] "terimakasih bantu etahu profit jual"
#> [9] "akun nonaktif"
#> [10] "bagus"
#> [11] "bagus bantu"
#> [12] "bantu"
#> [13] "bantu"
#> [14] "aplikasi kasir"
#> [15] "semoga gratis"
#> [16] "ajar jual"
#> [17] "apik online aneh"
#> [18] "aplikasi kasir pakai mudah praktis"
#> [19] "keren"
#> [20] "bagus"
==== DATA COPY ====
data_copy <- data
head(data_copy)library(stringr)
# Menghapus kata-kata yang kurang penting/stopword tambahan dari teks ulasan
stopwords <- c("kenapa", "banget", "iya", "deh", "engah", "sih", "kadang", "biar", "sete", "kan", "memang", "kayak", "apalag", "erti", "etahu", "mbangkan", "a", "dai", "hehe", "ima", "erti", "pela", "laki-lak", "tahank", "and", "an", "is", "mbak", "mas", "ecek", "b", "for", "i", "by", "insyaa", "NA", "i", "nge", "not", "oi", "ta", "use", "ena", "halo", "me", "maka", "sangant", "t", "sekaliitterima", "tata", "masya", "sangat", "sangatt", "p", "teman-tem", "sepert", "sekali", "dalam", "kakak", "alias", "kak")
data_copy$content <- str_replace_all(data_copy$content, paste0("\\b", paste(stopwords, collapse = "\\b|\\b"), "\\b"), "")data_copy# Mencari baris yang mengandung empty string pada kolom "content"
empty_rows <- which(data_copy$content == "")
# Menampilkan data yang mengandung empty string pada kolom "content"
empty_data <- data_copy[empty_rows, ]
empty_data# Mengambil data selain empty string
data_copy <- data_copy[data_copy$content != "", ]
dim(data_copy)#> [1] 14027 8
head(data_copy, 15)Setelah data teks dirasa sudah cukup bersih dan memenuhi ekspektasi, maka kita dapat melanjutkan proses berikutnya untuk menganalisis dan memodelkan data. Di awal kita mendefinisikan 15.000 row data, namun setelah melalui proses yang cukup panjang kini tersisa 14.027 row.
data_copy <- data_copy %>% select(c("content", "content_real", "score", "at", "review_date", "review_day", "review_hour"))
glimpse(data_copy)#> Rows: 14,027
#> Columns: 7
#> $ content <chr> "mudah simpel", "bantu ", "senang aplikasi bantu usaha ke…
#> $ content_real <chr> "Mudah digunakan dan simple", "Sangat membantu sekali", "…
#> $ score <int> 5, 5, 5, 5, 5, 4, 5, 5, 1, 5, 5, 5, 4, 5, 5, 5, 5, 5, 5, …
#> $ at <dttm> 2023-06-21 22:35:34, 2023-06-21 17:48:50, 2023-06-21 15:…
#> $ review_date <date> 2023-06-21, 2023-06-21, 2023-06-21, 2023-06-21, 2023-06-…
#> $ review_day <ord> Wednesday, Wednesday, Wednesday, Wednesday, Wednesday, We…
#> $ review_hour <int> 22, 17, 15, 14, 8, 5, 2, 21, 18, 15, 9, 23, 18, 20, 9, 21…
Exploratory Data Analysis (EDA) adalah suatu pendekatan analisis yang digunakan untuk memahami data secara visual dan deskriptif sebelum dilakukan proses pemodelan atau analisis yang lebih lanjut. Tujuan utama dari EDA adalah untuk menemukan pola, tren, dan mengidentifikasi karakteristik penting dari data yang ada. Proses EDA kali ini akan dibagi dalam 2 section. Pada section pertama, kita akan menggunakan sampel data mentah sebanyak 15.000 observasi untuk melakukan analisis EDA terhadap pola sebaran rating yang diberikan oleh pengguna terhadap aplikasi KasirPintar. Scope utama dari section pertama adalah rating yang diberikan pengguna. Sedangkan, pada section kedua, kita akan menggunakan 14.045 baris data teks yang telah dibersihkan untuk melakukan analisis EDA yang lebih mendalam terkait ulasan pengguna terhadap aplikasi KasirPintar. Scope utama dari section kedua adalah analisis ulasan pengguna yang terkandung dalam data teks review. Kita akan menjelajahi berbagai aspek dari data teks ini, termasuk frekuensi kata-kata, wordcloud, menggali insight lebih dalam tentang pandangan pengguna terhadap aplikasi KasirPintar.
Data Preparation
data_mentah <- read.csv("kasirpintar15k.csv")
glimpse(data_mentah)#> Rows: 15,000
#> Columns: 12
#> $ X <int> 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,…
#> $ reviewId <chr> "6a258c58-a76f-40a6-93c3-0fbafe6c2486", "f93242e9…
#> $ userName <chr> "Darda Tatsauri", "Erwin SE", "Wawan Kurniawan", …
#> $ userImage <chr> "https://play-lh.googleusercontent.com/a/AAcHTteP…
#> $ content <chr> "Mudah digunakan dan simple", "Sangat membantu se…
#> $ score <int> 5, 5, 5, 5, 4, 5, 4, 5, 5, 1, 5, 5, 5, 4, 5, 5, 5…
#> $ thumbsUpCount <int> 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0…
#> $ reviewCreatedVersion <chr> "4.9.8", "4.9.8", "4.9.8", "4.9.8", "4.9.8", "4.9…
#> $ at <chr> "2023-06-21 22:35:34", "2023-06-21 17:48:50", "20…
#> $ replyContent <chr> "Hai Darda Tatsauri! terima kasih telah menggunak…
#> $ repliedAt <chr> "2023-06-22 09:14:30", "2023-06-22 09:14:10", "20…
#> $ appVersion <chr> "4.9.8", "4.9.8", "4.9.8", "4.9.8", "4.9.8", "4.9…
data_mentah <-data_mentah %>% select(c("score", "at")) %>%
mutate(score = as.factor(score),
at = ymd_hms(at)) %>%
glimpse()#> Rows: 15,000
#> Columns: 2
#> $ score <fct> 5, 5, 5, 5, 4, 5, 4, 5, 5, 1, 5, 5, 5, 4, 5, 5, 5, 5, 5, 5, 5, 5…
#> $ at <dttm> 2023-06-21 22:35:34, 2023-06-21 17:48:50, 2023-06-21 15:28:31, …
data_mentah$review_date <- as.Date(data_mentah$at, format = "%Y-%m-%d")data_mentah$year <- year(data_mentah$review_date)
data_mentah$month <- month(data_mentah$review_date)
data_mentah$day <- day(data_mentah$review_date)glimpse(data_mentah)#> Rows: 15,000
#> Columns: 6
#> $ score <fct> 5, 5, 5, 5, 4, 5, 4, 5, 5, 1, 5, 5, 5, 4, 5, 5, 5, 5, 5, 5…
#> $ at <dttm> 2023-06-21 22:35:34, 2023-06-21 17:48:50, 2023-06-21 15:2…
#> $ review_date <date> 2023-06-21, 2023-06-21, 2023-06-21, 2023-06-21, 2023-06-2…
#> $ year <dbl> 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023…
#> $ month <dbl> 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6…
#> $ day <int> 21, 21, 21, 21, 21, 21, 21, 21, 20, 20, 20, 20, 19, 19, 18…
range(data_mentah$review_date)#> [1] "2018-11-08" "2023-06-21"
Kita akan mencari informasi mengenai frekuensi masuknya review/ulasan setiap tahun. Berdasarkan data yang kita miliki, total jumlah ulasan yang masuk selama periode dari tanggal 11 Agustus 2018 hingga tanggal 21 Juni 2023 pukul adalah 15.000 ulasan. Dengan menggunakan data ini, kita dapat melakukan analisis untuk mengetahui frekuensi masuknya ulasan tersebut di setiap tahun. Dalam hal ini, kita akan mengelompokkannya berdasarkan tahun masuknya.
rev_per_th <- data_mentah %>%
group_by(year) %>%
summarise(freq = n())
rev_per_thlibrary(ggplot2)
library(glue)
library(plotly)
rev_per_th <- rev_per_th %>% mutate(label = glue("Year : {year}
Freq : {freq} "))
plot1 <- ggplot(rev_per_th, aes(x = year, y = freq, text = label)) +
geom_col(fill = "#37517a") +
geom_col(data = rev_per_th %>%
filter(year == 2020), fill="#751a2f") +
labs(title = "Jumlah Review Masuk per Tahun",
x = "Tahun",
y = "Jumlah Review")
ggplotly(p = plot1, tooltip = "text")💡 Insight :
Tahun dengan jumlah ulasan tertinggi adalah tahun 2020 dengan 5.082 ulasan. Ini menunjukkan bahwa pada tahun 2020, aplikasi Kasir Pintar mungkin mencapai tingkat popularitas atau penggunaan yang lebih tinggi, yang mendorong lebih banyak pengguna untuk memberikan ulasan. Namun perlu diperhatikan, jumlah ulasan yang masuk pada aplikasi Kasir Pintar mengalami penurunan sejak tahun tersebut. Penggunaan aplikasi Kasir Pintar mungkin mengalami peningkatan pada tahun 2020 karena banyak pedagang dan pemilik bisnis yang beralih ke penjualan online, mengingat juga pada saat itu adalah masa pandemi yang mengharuskan semua aktivitas bisnis dilakukan secara online.
Sekarang kita akan mencoba melihat bagaimana sebaran rating yang diberikan oleh pengguna di setiap tahunnya. Dengan mengamati sebaran rating dari waktu ke waktu, kita dapat mengidentifikasi pola atau tren apakah aplikasi Kasir Pintar semakin baik atau sebaliknya dalam persepsi pengguna. Sebelumnya kita akan mengklasifikasikan rating yang diberikan pengguna ke dalam 3 kategori sentimen, yakni positif, negatif dan netral.
data_mentah <- data_mentah %>%
mutate(sentimen = case_when(
score %in% c(1, 2) ~ "Negatif",
score == 3 ~ "Netral",
score %in% c(4, 5) ~ "Positif"
))glimpse(data_mentah)#> Rows: 15,000
#> Columns: 7
#> $ score <fct> 5, 5, 5, 5, 4, 5, 4, 5, 5, 1, 5, 5, 5, 4, 5, 5, 5, 5, 5, 5…
#> $ at <dttm> 2023-06-21 22:35:34, 2023-06-21 17:48:50, 2023-06-21 15:2…
#> $ review_date <date> 2023-06-21, 2023-06-21, 2023-06-21, 2023-06-21, 2023-06-2…
#> $ year <dbl> 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023…
#> $ month <dbl> 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6…
#> $ day <int> 21, 21, 21, 21, 21, 21, 21, 21, 20, 20, 20, 20, 19, 19, 18…
#> $ sentimen <chr> "Positif", "Positif", "Positif", "Positif", "Positif", "Po…
rat_per_th <- data_mentah %>%
mutate(sentimen = as.factor(sentimen)) %>%
group_by(year, sentimen) %>%
summarize(freq = n())
rat_per_thrat_per_th <- rat_per_th %>%
mutate(label = glue(
"Tahun: {year}
Frekuensi: {freq}
Sentimen: {sentimen}"
))
plot2 <- ggplot(rat_per_th, aes(y = freq, x = year)) +
geom_line(aes(group = sentimen, col = sentimen, label = sentimen), show.legend = TRUE) +
geom_point(aes(col = sentimen, text = label), show.legend = TRUE) +
labs(y = NULL,
x = "Tahun",
title = "Sentimen di Setiap Tahun") +
theme_minimal() +
theme(axis.text.x = element_text(face = "bold")) +
scale_color_manual(values = c("Positif" = "green", "Negatif" = "red", "Netral" = "blue"),
labels = c("Negatif", "Netral", "Positif"),
name = "Sentimen")
ggplotly(p = plot2, tooltip = "text")💡 Insight :
Kita tau sebelumnya bahwa tahun 2020 merupakan tahun dengan jumlah review yang masuk paling banyak diantara yang lain. Dari line plot di atas, dapat diamati bahwa pada tahun 2020 terdapat sebaran sentimen yang signifikan terhadap aplikasi Kasir Pintar. Dalam tahun tersebut, sebanyak 4623 ulasan (92% dari total ulasan tahun 2020) merupakan ulasan dengan sentimen positif. Sentimen positif yang signifikan pada tahun 2020 dapat dihubungkan dengan beberapa faktor. Pertama, sebagai respons terhadap situasi pandemi COVID-19 yang melanda dunia, banyak usaha dan pedagang beralih ke model bisnis online. Dalam konteks ini, Kasir Pintar menjadi solusi yang efektif dan membantu dalam menjalankan operasional bisnis secara digital, sehingga mendapatkan apresiasi dan ulasan positif dari pengguna yang mengalami keberhasilan dalam mengadaptasi bisnis mereka.
Melirik ke tahun 2023, jumlah ulasan dengan sentimen positif sebesar 502, yang mencakup 90% dari total ulasan pada tahun tersebut. Meskipun persentase ulasan positif bisa dikatakan masih tinggi, namun terdapat penurunan dibandingkan dengan tahun 2020 yang mencapai 92%. Penurunan ini bisa saja terjadi karena beberapa faktor berikut :
Pertama, setelah masa pandemi COVID-19, di mana pada tahun 2020 terjadi lonjakan penggunaan aplikasi Kasir Pintar karena adopsi model bisnis online yang masif, mungkin terjadi penurunan intensitas penggunaan atau kebutuhan pengguna terhadap aplikasi tersebut. Setelah adaptasi awal pada tahun 2020, pengguna mungkin telah merasa lebih terbiasa dan tidak lagi memberikan ulasan dengan intensitas yang sama seperti pada tahun sebelumnya.
Selain itu, dengan berjalannya waktu, persaingan di industri aplikasi kasir dan manajemen bisnis semakin ketat. Munculnya pesaing baru atau peningkatan kualitas layanan dari pesaing yang sudah ada dapat mempengaruhi persepsi pengguna terhadap Kasir Pintar. Pengguna mungkin juga menjadi lebih kritis dan menuntut fitur atau pengalaman pengguna yang lebih baik dari aplikasi tersebut.
Meskipun terjadi penurunan persentase ulasan positif pada tahun 2023 (dibandingkan dengan tahun 2020), penting untuk dicatat bahwa Kasir Pintar masih mendapatkan sebagian besar ulasan dengan sentimen positif. Ini menunjukkan bahwa sebagian besar pengguna masih puas dengan pengalaman mereka menggunakan aplikasi tersebut. Bagi Kasir Pintar, penting untuk terus meningkatkan kualitas layanan dan berinovasi agar tetap relevan di pasar yang kompetitif dan mempertahankan kepuasan pengguna yang tinggi ini.
avg_rating_per_year <- data_mentah %>%
mutate(score = as.integer(score)) %>%
group_by(year) %>%
summarise(avg_rating = round(mean(score),2))avg_rating_per_year <- avg_rating_per_year %>% mutate(label = glue(
"Tahun: {year}
Avg. Rate: {avg_rating}"
))
plot3 <- ggplot(avg_rating_per_year, aes(x = year, y = avg_rating, group = 1)) +
geom_line(color = "steelblue") +
geom_point(color = "steelblue", aes(text = label)) +
labs(x = "Tahun", y = "Skor Rata-rata Rating", title = "Skor Rata-rata Rating per Tahun") +
theme_minimal()
ggplotly(p = plot3, tooltip = "text")💡 Insight :
Dari plot diatas, terlihat adanya sedikit penurunan rata-rata rating aplikasi Kasir Pintar dari tahun 2023 (4.72) ke tahun 2023 (4.58). Meskipun penurunan ini kecil dan mungkin tidak signifikan secara statistik, tetap penting untuk dicermati. Kaitkannya dengan business knowledge, penurunan rata-rata rating dapat mengindikasikan adanya beberapa faktor atau perubahan yang mempengaruhi persepsi pengguna terhadap aplikasi Kasir Pintar. Mungkin ada beberapa masalah atau kekurangan yang perlu diperbaiki atau ditingkatkan dalam aplikasi tersebut.
Jika kita hanya melihat secara visual dari plot diatas, kita belum bisa melihat apasih sebenarnya yang menjadi “problematik” pada aplikasi Kasir Pintar, yang menyebabkan akhir-akhir ini terjadi penurunan dalam hal persepsi pengguna. Pertanyaan tersebut akan dijawab ketika kita masuk lebih jauh mengenal data ulasan dari pengguna. Kemudian, nantinya kita juga akan memodelkan data ulasan pengguna ini kedalam topik-topik tertentu, sehingga kita bisa melihat lebih detail isu apa yang terjadi pada aplikasi dan layanan Kasir Pintar.
Pada section kedua ini, kami akan menggunakan 14.045 entri ulasan pengguna yang telah melewati proses pembersihan data sebelumnya. Tujuan utama dari bagian ini adalah untuk melakukan analisis yang lebih mendalam terkait ulasan pengguna mengenai aplikasi KasirPintar. Kita akan menjelajahi berbagai aspek dari data teks ini, seperti menghitung frekuensi kata-kata yang muncul, membuat visualisasi wordcloud, serta menggali insight yang lebih mendalam mengenai pandangan pengguna terhadap aplikasi KasirPintar dan akan dikaitkan dengan hasil analisis yang telah didapatkan pada section pertama.
data_clean <- data_copy
head(data_clean)Plot Top 10 Most Frequent Words memberikan representasi visual tentang kata-kata yang muncul paling sering dalam ulasan teks aplikasi Kasir Pintar. Grafik batang menampilkan frekuensi kemunculan setiap kata, yang memungkinkan kita mengidentifikasi kata-kata yang paling sering muncul dalam ulasan.
library(tidytext)
# Load the data and perform text processing
data_clean %>%
unnest_tokens(word, content) %>%
count(word, sort = TRUE) %>%
top_n(10) %>%
ggplot(aes(x = reorder(word, n), y = n)) +
geom_col(fill = "steelblue") +
labs(x = "Words", y = "Frequency", title = "Top 10 Most Frequent Words") +
coord_flip() +
theme_minimal()
Secara umum, kata-kata seperti “bantu”, “bagus”, “aplikasi”, “oke”, dan
kata-kata lainnya yang masuk dalam daftar “10 Kata Paling Sering Muncul”
menunjukkan adanya sentimen positif terkait dengan pengalaman pengguna
terhadap aplikasi Kasir Pintar. Kata-kata tersebut mencerminkan ungkapan
kepuasan, apresiasi, dan kepercayaan pengguna terhadap kualitas dan
manfaat aplikasi. Namun, dengan barplot ini kita belum menemukan ada
kata-kata negatif. Padahal sebenarnya, esensi kita melakukan analisis
ini adalah kita ingin mengetahui dalam hal apa KasirPintar dinilai
negatif oleh pengguna. Kata-kata negatif dapat mengungkapkan aspek-aspek
yang kurang memuaskan atau memiliki kelemahan dalam aplikasi Kasir
Pintar. Dengan mengetahui kelemahan-kelemahan ini, tim pengembang atau
perusahaan dapat mengambil tindakan perbaikan yang relevan untuk
meningkatkan pengalaman pengguna. Sebagai lanjutannya, kita akan
menampilkan visualisasi wordcloud terhadap keseluruhan text, suoaya kita
bisa melihat lebih luas lagi terkait sebaran kata-kata yang ada.
# Install paket 'wordcloud2' jika belum terpasang
# install.packages("wordcloud2")
library(wordcloud2)
# Menggabungkan semua teks review menjadi satu string
all_text <- paste(data_clean$content, collapse = " ")
# Menghitung frekuensi kata-kata
word_freq <- table(strsplit(all_text, "\\s+"))
# Mengubah data frekuensi kata-kata menjadi format yang sesuai untuk wordcloud2
wordcloud_data <- data.frame(word = names(word_freq), freq = as.integer(word_freq))
# Membuat wordcloud dengan wordcloud2
wordcloud2(wordcloud_data, size = 1.5)Dari visualisasi wordcloud di atas, kehadiran kata-kata negatif masih cukup sulit ditemukan. Kata-kata negatif yang mungkin muncul dalam ulasan pengguna tentang aplikasi Kasir Pintar mungkin memiliki frekuensi yang lebih rendah dibandingkan dengan kata-kata positif atau netral. Untuk bisa mendapatkan informasi terkait kata-kata negatif, kita akan coba melakukan visualisasi wordcloud secara khusus pada data ulasan dengan rating 1 dan 2.
data_clean <- data_clean %>%
mutate(sentimen = case_when(
score %in% c(1, 2) ~ "Negatif",
score == 3 ~ "Netral",
score %in% c(4, 5) ~ "Positif"
)) %>% glimpse()#> Rows: 14,027
#> Columns: 8
#> $ content <chr> "mudah simpel", "bantu ", "senang aplikasi bantu usaha ke…
#> $ content_real <chr> "Mudah digunakan dan simple", "Sangat membantu sekali", "…
#> $ score <int> 5, 5, 5, 5, 5, 4, 5, 5, 1, 5, 5, 5, 4, 5, 5, 5, 5, 5, 5, …
#> $ at <dttm> 2023-06-21 22:35:34, 2023-06-21 17:48:50, 2023-06-21 15:…
#> $ review_date <date> 2023-06-21, 2023-06-21, 2023-06-21, 2023-06-21, 2023-06-…
#> $ review_day <ord> Wednesday, Wednesday, Wednesday, Wednesday, Wednesday, We…
#> $ review_hour <int> 22, 17, 15, 14, 8, 5, 2, 21, 18, 15, 9, 23, 18, 20, 9, 21…
#> $ sentimen <chr> "Positif", "Positif", "Positif", "Positif", "Positif", "P…
# Mengambil data sentimen negatif
data_clean_neg <- data_clean %>% filter(sentimen == "Negatif") %>%
glimpse()#> Rows: 486
#> Columns: 8
#> $ content <chr> "akun nonaktif", "akun nonaktif masuk mbali", "coba dafta…
#> $ content_real <chr> "Akun dinonaktifkan peeh", "Mengapa akun saiia dinon akti…
#> $ score <int> 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, …
#> $ at <dttm> 2023-06-20 18:37:11, 2023-06-08 12:39:19, 2023-06-04 00:…
#> $ review_date <date> 2023-06-20, 2023-06-08, 2023-06-04, 2023-05-26, 2023-05-…
#> $ review_day <ord> Tuesday, Thursday, Sunday, Friday, Monday, Friday, Friday…
#> $ review_hour <int> 18, 12, 0, 6, 12, 18, 6, 13, 19, 9, 20, 17, 9, 16, 21, 23…
#> $ sentimen <chr> "Negatif", "Negatif", "Negatif", "Negatif", "Negatif", "N…
library(wordcloud)
library(wordcloud2 )
# Menggabungkan semua teks review menjadi satu string
all_text_neg <- paste(data_clean_neg$content, collapse = " ")
# Menghitung frekuensi kata-kata
word_freq_neg <- table(strsplit(all_text_neg, "\\s+"))
# Mengubah data frekuensi kata-kata menjadi format yang sesuai untuk wordcloud2
wordcloud_data_neg <- data.frame(word = names(word_freq_neg), freq = as.integer(word_freq_neg))
wordcloud_data_neg <- wordcloud_data_neg[order(-wordcloud_data_neg$freq), ]
# Create a word cloud plot
wordcloud_plot_neg <- wordcloud(wordcloud_data_neg$word, wordcloud_data_neg$freq, scale = c(6, 0.3), random.order = FALSE)# Convert the word cloud plot to a ggplot object
ggplot_wordcloud_neg <- ggplotly(wordcloud_plot_neg)
# Display the ggplot word cloud
ggplot_wordcloud_negWordcloud di atas menampilkan kata-kata negatif yang muncul pada teks ulasan pengguna tentang aplikasi Kasir Pintar. Kata-kata seperti “ribet”, “susah”, “bug”, “sampah”, “jelek”, “payah”, dan sebagainya mengindikasikan adanya beberapa isu atau masalah yang sering dihadapi oleh pengguna. Hal ini menunjukkan bahwa ada beberapa area yang perlu diperbaiki atau ditingkatkan dalam pengalaman pengguna dengan aplikasi tersebut.
Dengan code dibawah ini kita coba visualisasikan kata apa saja yang paling berkontribusi terhadap sentimen negatif (rating 1 dan 2).
data_clean_neg %>%
unnest_tokens(word, content) %>%
count(word, sort = TRUE) %>%
top_n(10) %>%
ggplot(aes(x = reorder(word, n), y = n)) +
geom_col(fill = "steelblue") +
labs(x = "Words", y = "Frequency", title = "Top 10 Most Frequent Words in Negatif Sentimen") +
coord_flip() +
theme_minimal()Selanjutnya kita akan melakukan pemodelan topik terkait isu ataupun hal lain yang sering dibicarakan oleh pengguna terkait dengan sentimen negatif ini. Kita akan melakukan pemodelan dengan metode Latent Direchlet Allocation (LDA).
Pemodelan Topik (topic modeling) adalah teknik analisis data yang
digunakan untuk mengidentifikasi dan mengelompokkan topik-topik yang
tersembunyi dalam koleksi dokumen atau teks. Tujuan utama dari topic
modeling adalah untuk mengungkapkan struktur topik yang ada dalam data
teks dan mengelompokkan dokumen ke dalam topik-topik yang saling
terkait. Salah satu metode populer dalam topic modeling adalah Latent
Dirichlet Allocation (LDA). LDA mengasumsikan bahwa setiap dokumen dapat
terdiri dari beberapa topik yang tersembunyi, dan setiap kata dalam
dokumen dapat dikaitkan dengan salah satu topik tersebut. Melalui proses
iteratif, LDA menghasilkan distribusi probabilitas kata-kata untuk
setiap topik dan distribusi probabilitas topik untuk setiap dokumen.
Nilai topik pada model LDA ditentukan dengan nilai k, serta
beberapa komponen yang bisa diatur dan disesuaikan dengan analisis lebih
lanjut.
LDA mengasumsikan proses generatif untuk dokumen-dokumen, di mana setiap dokumen merupakan campuran dari beberapa topik dan setiap kata dalam dokumen dihasilkan dari salah satu topik tersebut. Pendekatan pemodelan generatif ini memungkinkan LDA untuk secara efektif menggambarkan struktur yang mendasari korpus. LDA dikenal karena kemampuannya untuk secara otomatis menemukan topik-topik tersembunyi dalam korpus tanpa memerlukan pengetahuan sebelumnya atau data berlabel. LDA mengidentifikasi topik berdasarkan pola kemunculan kata-kata di seluruh dokumen. Sebelum memodelkan data teks ulasan yang ada, kita terlebih dahulu harus membentuk Document-Term Matrix (DTM). DTM adalah representasi matematis yang menggambarkan frekuensi kemunculan kata-kata dalam setiap dokumen dalam koleksi teks.
library(tm)
# Membangun Corpus
corpus_neg <- Corpus(VectorSource(data_clean_neg$content))
# Membangun matriks Document-Term Matrix (DTM)
dtm_neg <- DocumentTermMatrix(corpus_neg)
# Menampilkan DTM
inspect(dtm_neg)#> <<DocumentTermMatrix (documents: 486, terms: 402)>>
#> Non-/sparse entries: 2334/193038
#> Sparsity : 99%
#> Maximal term length: 14
#> Weighting : term frequency (tf)
#> Sample :
#> Terms
#> Docs aplikasi bagus barang coba daftar data hilang masuk pakai transaksi
#> 141 0 0 1 0 0 0 0 0 3 0
#> 144 1 0 0 0 0 3 2 0 0 0
#> 145 0 0 2 0 0 0 0 0 0 0
#> 153 4 0 0 0 0 2 2 0 2 0
#> 160 1 0 1 0 0 0 0 0 0 0
#> 176 1 0 0 0 0 0 0 0 0 0
#> 250 6 0 0 0 0 0 0 0 3 0
#> 267 2 0 0 2 0 0 0 0 2 1
#> 308 3 0 0 2 0 0 0 0 0 2
#> 68 2 0 0 0 0 0 0 0 0 0
Berdasarkan informasi yang dihasilkan diatas, DTM kita memiliki 486 dokumen/ulasan dengan sentimen negatif dan 402 kata yang berbeda.
Sebagai model dasar, kita akan menggunakan nilai k secara subjektif untuk pertama kali. Untuk base model ini, kita akan menggunakan 2000 iterasi dan 1000 burn-in. Parameter burnin mengacu pada jumlah iterasi awal yang diabaikan dalam model LDA. Pada awal iterasi, model LDA mungkin belum mencapai konvergensi atau estimasi yang stabil. Oleh karena itu, dengan mengabaikan sejumlah iterasi awal (burn-in), kita dapat menghindari pengaruh dari kondisi awal yang mungkin kurang optimal. Pada model base pertama ini, jumlah burn-in yang digunakan adalah 1000, yang berarti 1000 iterasi awal akan diabaikan.
Sebelum melakukan pemodelan, kita harus memastikan bahwa di dalam
dtm_neg kita tidak ada docs yang tidak berisi/zero entry
karena model LDA needs to contain at least one non-zero entry. Zero
entry biasanya disebabkan karena ada data yang isinya whitespace.
Dibawah ini adalah contoh data yang tidak berisi/zero entry.
data_clean[4059,]data_clean_neg# Hitung jumlah entri non-nol pada setiap baris
non_zero_counts_neg <- rowSums(as.matrix(dtm_neg) > 0)
# Identifikasi baris dengan jumlah entri non-nol nol
empty_rows_neg <- which(non_zero_counts_neg == 0)
# Tampilkan indeks dokumen yang tidak memiliki entri non-nol
cat("Dokumen zero entry:", empty_rows_neg, "\n")#> Dokumen zero entry:
# Code untuk filter baris dengan entri non-nol
# dtm_filtered <- dtm[rowSums(as.matrix(dtm_neg)) > 0, ]dtm_lda_neg <- Matrix::Matrix(as.matrix(dtm_neg), sparse = T)library(textmineR)
# Mencoba membuat model awal dengan k = 3, iterations = 2000, burnin = 1000
set.seed(123)
lda_base_neg <- FitLdaModel(dtm = dtm_lda_neg,
k = 3,
iterations = 2000,
burnin = 1000,
calc_coherence = T
)
str(lda_base_neg)#> List of 7
#> $ phi : num [1:3, 1:402] 0.0000687 0.0000559 0.0586196 0.0000687 0.0000559 ...
#> ..- attr(*, "dimnames")=List of 2
#> .. ..$ : chr [1:3] "t_1" "t_2" "t_3"
#> .. ..$ : chr [1:402] "akun" "nonaktif" "masuk" "mbali" ...
#> $ theta : num [1:486, 1:3] 0.0769 0.0303 0.0435 0.0159 0.0303 ...
#> ..- attr(*, "dimnames")=List of 2
#> .. ..$ : chr [1:486] "1" "2" "3" "4" ...
#> .. ..$ : chr [1:3] "t_1" "t_2" "t_3"
#> $ gamma : num [1:3, 1:402] 0.00135 0.00138 0.99728 0.01632 0.01664 ...
#> ..- attr(*, "dimnames")=List of 2
#> .. ..$ : chr [1:3] "t_1" "t_2" "t_3"
#> .. ..$ : chr [1:402] "akun" "nonaktif" "masuk" "mbali" ...
#> $ data :Formal class 'dgCMatrix' [package "Matrix"] with 6 slots
#> .. ..@ i : int [1:2334] 0 1 3 7 10 15 18 20 59 67 ...
#> .. ..@ p : int [1:403] 0 32 36 83 92 135 174 180 196 307 ...
#> .. ..@ Dim : int [1:2] 486 402
#> .. ..@ Dimnames:List of 2
#> .. .. ..$ Docs : chr [1:486] "1" "2" "3" "4" ...
#> .. .. ..$ Terms: chr [1:402] "akun" "nonaktif" "masuk" "mbali" ...
#> .. ..@ x : num [1:2334] 1 1 1 1 1 1 1 1 1 2 ...
#> .. ..@ factors : list()
#> $ alpha : Named num [1:3] 0.1 0.1 0.1
#> ..- attr(*, "names")= chr [1:3] "t_1" "t_2" "t_3"
#> $ beta : Named num [1:402] 0.05 0.05 0.05 0.05 0.05 0.05 0.05 0.05 0.05 0.05 ...
#> ..- attr(*, "names")= chr [1:402] "akun" "nonaktif" "masuk" "mbali" ...
#> $ coherence: Named num [1:3] 0.0326 0.1153 0.1044
#> ..- attr(*, "names")= chr [1:3] "t_1" "t_2" "t_3"
#> - attr(*, "class")= chr "lda_topic_model"
Berikut penjelasan lebih detail tentang atribut-atribut yang diperoleh dari Model LDA :
phi : Atribut “phi” merupakan probabilitas posterior
per-topik-per-kata. Ini berarti untuk setiap topik, kita dapat melihat
probabilitas kemunculan kata-kata tertentu yang terkait dengan topik
tersebut. Nilai yang lebih tinggi menunjukkan bahwa kata tersebut
memiliki hubungan yang kuat dengan topik tersebut.theta : Atribut “theta” merupakan probabilitas
posterior per-dokumen-per-topik. Ini memberikan informasi tentang
probabilitas sebuah dokumen terkait dengan setiap topik. Dengan kata
lain, untuk setiap dokumen, kita dapat melihat probabilitas kemunculan
topik-topik tertentu dalam dokumen tersebut. Nilai yang lebih tinggi
menunjukkan bahwa dokumen tersebut memiliki hubungan yang erat dengan
topik tersebut.alpha : Atribut “alpha” adalah probabilitas prior
per-dokumen-per-topik. Ini adalah nilai prior yang digunakan dalam model
LDA untuk mempengaruhi probabilitas distribusi topik dalam dokumen
tertentu. Alpha yang lebih tinggi menunjukkan bahwa dokumen cenderung
memiliki variasi topik yang lebih besar.beta : Atribut “beta” adalah probabilitas prior
per-topik-per-kata. Ini adalah nilai prior yang digunakan dalam model
LDA untuk mempengaruhi probabilitas distribusi kata dalam topik
tertentu. Beta yang lebih tinggi menunjukkan bahwa topik cenderung
memiliki variasi kata yang lebih besar.coherence : Atribut “coherence” mengukur kohesi
probabilitas dalam sebuah topik. Ini memberikan informasi tentang sejauh
mana kata-kata dalam topik tersebut saling terkait dan membentuk konsep
yang konsisten. Nilai yang lebih tinggi menunjukkan bahwa topik tersebut
memiliki kohesi yang lebih baik.Mari kita lihat 10 terms dengan nilai probabilitas terms paling tinggi di setiap topik-nya berdasarkan nilai phi. Kata-kata dengan phi tinggi memiliki frekuensi terbanyak dalam suatu topik.
GetTopTerms(lda_base_neg$phi, 15) %>%
as.data.frame() %>%
set_names(paste("Topic", 1:3))Seperti yang dapat kita lihat diatas, dengan menggunakan LDA, meskipun kita tidak memiliki label atau kelas yang sebenarnya, model dapat menghasilkan asosiasi antara kata-kata dan topik dengan meng-assign probabilitas. Jika kita perhatikan, terdapat pengulangan terms pada topik yang terbentuk. Sehingga dengan model base ini, kita masih cukup sulit untuk menginterpretasikan setiap topiknya.
mean(lda_base_neg$coherence)#> [1] 0.08411237
Kita juga bisa melihat nilai prevalensi setiap topik dalam
keseluruhan dokumen. Prevalensi yang dimaksud disini dapat dilihat dari
nilai theta, nilai ini memberikan informasi topik yang
paling sering muncul dalam korpus/dokumen. Dalam pengertian lain nilai
theta adalah probabilitas distribusi topik di seluruh
dokumen.
lda_base_neg$prevalence <- colSums(lda_base_neg$theta)/sum(lda_base_neg$theta)*100
lda_base_neg$prevalence#> t_1 t_2 t_3
#> 34.32457 34.94278 30.73265
Output tersebut memberikan nilai probabilitas setiap topik terhadap keseluruhan dokumen. Topik 2 dalam model base ini merupakan topik yang paling dominan dalam dokumen.
Dalam proses tuning ini, kita akan menambahkan beberapa parameter
seperti alpha dan beta ke dalam model. 1. alpha (prioritas
dokumen-topik) : Ia menentukan kejarangan distribusi dokumen-topik.
Nilai rendah alpha membuat distribusi dokumen-topik lebih jarang,
artinya setiap dokumen akan direpresentasikan oleh beberapa topik
dominan. Nilai tinggi alpha membuat distribusi dokumen-topik lebih
seragam, memungkinkan setiap dokumen memiliki campuran beberapa topik.
Menetapkan alpha dengan nilai rendah mendorong dokumen untuk lebih fokus
pada beberapa topik, sementara nilai tinggi mendorong penugasan topik
yang lebih beragam untuk setiap dokumen. 2. beta (prioritas
topik-kata) : Ia mengontrol kejarangan distribusi topik-kata. Nilai
rendah beta membuat distribusi topik-kata lebih jarang, artinya setiap
topik akan direpresentasikan oleh sejumlah kecil kata yang sangat
mungkin. Nilai tinggi beta membuat distribusi topik-kata lebih tidak
jarang, memungkinkan topik memiliki distribusi kata yang lebih luas.
Menetapkan beta dengan nilai rendah mendorong topik untuk lebih fokus
pada beberapa kata tertentu, sementara nilai tinggi memungkinkan topik
memiliki kisaran kata yang lebih luas.
Kita akan mencoba beberapa kombinasi nilai alpha dan beta, lalu melakukan evaluasi dengan melihat nilai coherence skornya.
Model I : alpha 0.05 & beta 0.05
avg.coherece1 <- function(list.num){
values <- c()
for(num in list.num){
set.seed(123)
model <- FitLdaModel(dtm = dtm_lda_neg,
k = num,
alpha = 0.05,
beta = 0.05,
iterations = 2000,
burnin = 1000,
calc_coherence = T)
average <- mean(model$coherence)
values <- append(values, average)
}
return(values)
}# menyimpan sekuen nilai K
k.list <- 2:10
model.list1 <- avg.coherece1(k.list)
model.df1 <- as.data.frame(model.list1)
model.df1 <- model.df1 %>% add_rownames()
print(model.df1)#> # A tibble: 9 × 2
#> rowname model.list1
#> <chr> <dbl>
#> 1 1 0.0670
#> 2 2 0.0885
#> 3 3 0.110
#> 4 4 0.108
#> 5 5 0.109
#> 6 6 0.111
#> 7 7 0.123
#> 8 8 0.0998
#> 9 9 0.112
Model II : alpha 0.2 & beta 0.05
avg.coherece2 <- function(list.num){
values <- c()
for(num in list.num){
set.seed(123)
model <- FitLdaModel(dtm = dtm_lda_neg,
k = num,
alpha = 0.2,
beta = 0.05,
iterations = 2000,
burnin = 1000,
calc_coherence = T)
average <- mean(model$coherence)
values <- append(values, average)
}
return(values)
}# menyimpan sekuen nilai K
k.list <- 2:10
model.list2 <- avg.coherece2(k.list)
model.df2 <- as.data.frame(model.list2)
model.df2 <- model.df2 %>% add_rownames()
print(model.df2)#> # A tibble: 9 × 2
#> rowname model.list2
#> <chr> <dbl>
#> 1 1 0.0549
#> 2 2 0.0841
#> 3 3 0.112
#> 4 4 0.101
#> 5 5 0.0808
#> 6 6 0.113
#> 7 7 0.106
#> 8 8 0.108
#> 9 9 0.0989
Model III : alpha 0.6 & beta 0.05
avg.coherece3 <- function(list.num){
values <- c()
for(num in list.num){
set.seed(123)
model <- FitLdaModel(dtm = dtm_lda_neg,
k = num,
alpha = 0.6,
beta = 0.05,
iterations = 2000,
burnin = 1000,
calc_coherence = T)
average <- mean(model$coherence)
values <- append(values, average)
}
return(values)
}# menyimpan sekuen nilai K
k.list <- 2:10
model.list3 <- avg.coherece3(k.list)
model.df3 <- as.data.frame(model.list3)
model.df3 <- model.df3 %>% add_rownames()
print(model.df3)#> # A tibble: 9 × 2
#> rowname model.list3
#> <chr> <dbl>
#> 1 1 0.0549
#> 2 2 0.0977
#> 3 3 0.113
#> 4 4 0.100
#> 5 5 0.0886
#> 6 6 0.106
#> 7 7 0.113
#> 8 8 0.119
#> 9 9 0.107
Model IV : alpha 0.7 & beta 0.05
avg.coherece4 <- function(list.num){
values <- c()
for(num in list.num){
set.seed(123)
model <- FitLdaModel(dtm = dtm_lda_neg,
k = num,
alpha = 0.7,
beta = 0.05,
iterations = 2000,
burnin = 1000,
calc_coherence = T)
average <- mean(model$coherence)
values <- append(values, average)
}
return(values)
}# menyimpan sekuen nilai K
k.list <- 2:10
model.list4 <- avg.coherece4(k.list)
model.df4 <- as.data.frame(model.list4)
model.df4 <- model.df4 %>% add_rownames()
print(model.df4)#> # A tibble: 9 × 2
#> rowname model.list4
#> <chr> <dbl>
#> 1 1 0.0549
#> 2 2 0.0819
#> 3 3 0.0947
#> 4 4 0.0939
#> 5 5 0.0842
#> 6 6 0.103
#> 7 7 0.113
#> 8 8 0.111
#> 9 9 0.108
Model V : alpha 0.9 & beta 0.05
avg.coherece5 <- function(list.num){
values <- c()
for(num in list.num){
set.seed(123)
model <- FitLdaModel(dtm = dtm_lda_neg,
k = num,
alpha = 0.9,
beta = 0.05,
iterations = 2000,
burnin = 1000,
calc_coherence = T)
average <- mean(model$coherence)
values <- append(values, average)
}
return(values)
}# menyimpan sekuen nilai K
k.list <- 2:10
model.list5 <- avg.coherece5(k.list)
model.df5 <- as.data.frame(model.list5)
model.df5 <- model.df5 %>% add_rownames()
print(model.df5)#> # A tibble: 9 × 2
#> rowname model.list5
#> <chr> <dbl>
#> 1 1 0.0814
#> 2 2 0.0691
#> 3 3 0.0940
#> 4 4 0.0852
#> 5 5 0.0979
#> 6 6 0.113
#> 7 7 0.109
#> 8 8 0.114
#> 9 9 0.117
Berdasarkan percobaan beberapa kombinasi alpha dan beta yang telah dilakukan, nilai rata-rata coherence tertinggi diberikan saat nilai alpha = 0.05, beta = 0.05, dan k = 8 (Model I) dengan nilai 0.1228.
set.seed(123)
model_1_best <- FitLdaModel(dtm = dtm_lda_neg,
k = 8,
alpha = 0.05,
beta = 0.05,
iterations = 2000,
burnin = 1000,
calc_coherence = T)
mean(model_1_best$coherence)#> [1] 0.1228306
top_topic <- GetTopTerms(model_1_best$phi, 30) %>%
as.data.frame() %>%
set_names(paste("Topic", 1:8))
top_topiclibrary(tidyverse)
library(ggwordcloud)
head(top_topic, 10) %>%
rownames_to_column("id") %>%
mutate(id = as.numeric(id)) %>%
pivot_longer(-id, names_to = "topic", values_to = "term")%>%
ggplot(aes(label = term, size = rev(id), color = topic, alpha = rev(id))) +
geom_text_wordcloud(seed = 123) +
facet_wrap(~topic, scales = "free") +
scale_alpha_continuous(range = c(0.4, 1)) +
theme_minimal() +
theme(strip.background = element_rect(fill = "black"),
strip.text.x = element_text(colour = "white"))Berdasarkan wordcloud diatas, terlihat bahwa kata-kata positif muncul di beberapa topik. Hal ini menyebabkan kita masih cukup sulit untuk menginterpretasikan sesuatu yang sedang dibahas pada topik tersebut dan sekilas tampaknya terdapat kesamaaan di beberapa topik.
Mengingat fokus analisis kita adalah mencari isu, maka langkah yang akan diambil adalah mengeliminasi kata-kata positif yang terkait dengan nilai kepuasan seperti “bagus”, “oke”, dsb. Setelah itu kita akan kembali membuat model LDA yang lebih fokus pada isu-isu yang berkaitan dengan Kasir Pintar.
data_clean_neg2 <- data_clean_neg# Membaca data text berisi kumpulan kata positif
postv <- readLines("positive.txt")
# Eliminasi terms positif dari dataframe data_clean
clean_postv <- removeWords(data_clean_neg2$content,
words = postv)
# Menghapus spasi berlebih
clean_postv <- trimws(clean_postv)
# Menyimpan data hasil eliminasi kata positif
data_clean_neg2 <- data_clean_neg2 %>%
mutate(content2 = clean_postv)data_clean_neg2#data_clean <- data_clean[, -which(names(data_clean) == "content2")]# Membangun Corpus
corpus_neg2 <- Corpus(VectorSource(data_clean_neg2$content2))
# Membangun matriks Document-Term Matrix (DTM)
dtm_corpus_neg2 <- DocumentTermMatrix(corpus_neg2)
# Menampilkan DTM
inspect(dtm_corpus_neg2)#> <<DocumentTermMatrix (documents: 486, terms: 347)>>
#> Non-/sparse entries: 1950/166692
#> Sparsity : 99%
#> Maximal term length: 14
#> Weighting : term frequency (tf)
#> Sample :
#> Terms
#> Docs akun aplikasi barang coba daftar data hilang masuk pakai transaksi
#> 141 0 0 1 0 0 0 0 0 3 0
#> 144 0 1 0 0 0 3 2 0 0 0
#> 145 0 0 2 0 0 0 0 0 0 0
#> 153 3 4 0 0 0 2 2 0 2 0
#> 176 0 1 0 0 0 0 0 0 0 0
#> 250 0 6 0 0 0 0 0 0 3 0
#> 267 0 2 0 2 0 0 0 0 2 1
#> 308 0 3 0 2 0 0 0 0 0 2
#> 382 0 0 0 0 0 4 0 0 0 0
#> 68 2 2 0 0 0 0 0 0 0 0
# Filter baris dengan entri non-nol
dtm_filtered_neg2 <- dtm_corpus_neg2[rowSums(as.matrix(dtm_corpus_neg2)) > 0, ]# Hitung jumlah entri non-nol pada setiap baris
non_zero_counts_neg_check <- rowSums(as.matrix(dtm_corpus_neg2) > 0)
# Identifikasi baris dengan jumlah entri non-nol nol
empty_rows_neg_check <- which(non_zero_counts_neg_check == 0)
# Tampilkan indeks dokumen yang tidak memiliki entri non-nol
cat("Dokumen zero entry:", empty_rows_neg_check, "\n")#> Dokumen zero entry: 14 39 69 116 133 152 159 180 184 185 186 194 204 217 229 244 270 277 280 285 318 321 325 328 332 344 347 352 355 368 373 384 386 388 390 392 395 401 409 412 413 414 415 417 422 431 441 442 443 455 458 459 460 467 468 473 475 477 478 481 482 486
# Contoh row yang kolom content2 nya tidak berisi/zero entry
data_clean_neg2[14,]# Menentukan indeks baris yang ingin diabaikan/yang kolom content2 nya zero entry
indeks_abaikan <- c(14, 39, 69, 116, 133, 152, 159, 180, 184, 185, 186, 194, 204, 217, 229, 244, 270, 277, 280, 285, 318, 321, 325, 328, 332, 344, 347, 352, 355, 368, 373, 384, 386, 388, 390, 392, 395, 401, 409, 412, 413, 414, 415, 417, 422, 431, 441, 442, 443, 455, 458, 459, 460, 467, 468, 473, 475, 477, 478, 481, 482, 486)
# Mengambil baris-baris kecuali indeks yang diberikan
data_neg_fix <- subset(data_clean_neg2, !row.names(data_clean_neg2) %in% indeks_abaikan)! Dataframe dari data_neg_fix nantinya akan dipakai saat implementasi
dim(data_neg_fix)#> [1] 424 9
inspect(dtm_filtered_neg2)#> <<DocumentTermMatrix (documents: 424, terms: 347)>>
#> Non-/sparse entries: 1950/145178
#> Sparsity : 99%
#> Maximal term length: 14
#> Weighting : term frequency (tf)
#> Sample :
#> Terms
#> Docs akun aplikasi barang coba daftar data hilang masuk pakai transaksi
#> 141 0 0 1 0 0 0 0 0 3 0
#> 144 0 1 0 0 0 3 2 0 0 0
#> 145 0 0 2 0 0 0 0 0 0 0
#> 153 3 4 0 0 0 2 2 0 2 0
#> 176 0 1 0 0 0 0 0 0 0 0
#> 250 0 6 0 0 0 0 0 0 3 0
#> 267 0 2 0 2 0 0 0 0 2 1
#> 308 0 3 0 2 0 0 0 0 0 2
#> 382 0 0 0 0 0 4 0 0 0 0
#> 68 2 2 0 0 0 0 0 0 0 0
dtm_lda_neg2 <- Matrix::Matrix(as.matrix(dtm_filtered_neg2), sparse = T)Pada tahapan berikutnya, kita akan kembali mencoba menjalankan parameter-parameter sebelumnya untuk melihat perubahan nilai coherence.
Model Ib : alpha 0.05 & beta 0.05
avg.coherece1b <- function(list.num){
values <- c()
for(num in list.num){
set.seed(123)
model <- FitLdaModel(dtm = dtm_lda_neg2,
k = num,
alpha = 0.05,
beta = 0.05,
iterations = 2000,
burnin = 1000,
calc_coherence = T)
average <- mean(model$coherence)
values <- append(values, average)
}
return(values)
}# menyimpan sekuen nilai K
k.list <- 2:10
model.list1b <- avg.coherece1b(k.list)
model.df1b <- as.data.frame(model.list1b)
model.df1b <- model.df1b %>% add_rownames()
print(model.df1b)#> # A tibble: 9 × 2
#> rowname model.list1b
#> <chr> <dbl>
#> 1 1 0.0588
#> 2 2 0.0495
#> 3 3 0.0873
#> 4 4 0.0944
#> 5 5 0.0901
#> 6 6 0.105
#> 7 7 0.102
#> 8 8 0.104
#> 9 9 0.0993
Model IIb : alpha 0.2 & beta 0.05
avg.coherece2b <- function(list.num){
values <- c()
for(num in list.num){
set.seed(123)
model <- FitLdaModel(dtm = dtm_lda_neg2,
k = num,
alpha = 0.2,
beta = 0.05,
iterations = 2000,
burnin = 1000,
calc_coherence = T)
average <- mean(model$coherence)
values <- append(values, average)
}
return(values)
}# menyimpan sekuen nilai K
k.list <- 2:10
model.list2b <- avg.coherece2b(k.list)
model.df2b <- as.data.frame(model.list2b)
model.df2b <- model.df2b %>% add_rownames()
print(model.df2b)#> # A tibble: 9 × 2
#> rowname model.list2b
#> <chr> <dbl>
#> 1 1 0.0568
#> 2 2 0.0790
#> 3 3 0.0822
#> 4 4 0.0845
#> 5 5 0.0960
#> 6 6 0.0824
#> 7 7 0.0866
#> 8 8 0.0904
#> 9 9 0.103
Model IIIb : alpha 0.6 & beta 0.05
avg.coherece3b <- function(list.num){
values <- c()
for(num in list.num){
set.seed(123)
model <- FitLdaModel(dtm = dtm_lda_neg2,
k = num,
alpha = 0.6,
beta = 0.05,
iterations = 2000,
burnin = 1000,
calc_coherence = T)
average <- mean(model$coherence)
values <- append(values, average)
}
return(values)
}# menyimpan sekuen nilai K
k.list <- 2:10
model.list3b <- avg.coherece3b(k.list)
model.df3b <- as.data.frame(model.list3b)
model.df3b <- model.df3b %>% add_rownames()
print(model.df3b)#> # A tibble: 9 × 2
#> rowname model.list3b
#> <chr> <dbl>
#> 1 1 0.0695
#> 2 2 0.0806
#> 3 3 0.0831
#> 4 4 0.0826
#> 5 5 0.0949
#> 6 6 0.117
#> 7 7 0.114
#> 8 8 0.0907
#> 9 9 0.0950
Model IVb : alpha 0.7 & beta 0.05
avg.coherece4b <- function(list.num){
values <- c()
for(num in list.num){
set.seed(123)
model <- FitLdaModel(dtm = dtm_lda_neg2,
k = num,
alpha = 0.7,
beta = 0.05,
iterations = 200,
burnin = 100,
calc_coherence = T)
average <- mean(model$coherence)
values <- append(values, average)
}
return(values)
}# menyimpan sekuen nilai K
k.list <- 2:10
model.list4b <- avg.coherece4b(k.list)
model.df4b <- as.data.frame(model.list4b)
model.df4b <- model.df4b %>% add_rownames()
print(model.df4b)#> # A tibble: 9 × 2
#> rowname model.list4b
#> <chr> <dbl>
#> 1 1 0.0677
#> 2 2 0.0676
#> 3 3 0.0904
#> 4 4 0.0764
#> 5 5 0.0838
#> 6 6 0.0847
#> 7 7 0.0913
#> 8 8 0.0948
#> 9 9 0.0901
Model Vb : alpha 0.9 & beta 0.05
avg.coherece5b <- function(list.num){
values <- c()
for(num in list.num){
set.seed(123)
model <- FitLdaModel(dtm = dtm_lda_neg2,
k = num,
alpha = 0.9,
beta = 0.05,
iterations = 2000,
burnin = 1000,
calc_coherence = T)
average <- mean(model$coherence)
values <- append(values, average)
}
return(values)
}# menyimpan sekuen nilai K
k.list <- 2:10
model.list5b <- avg.coherece5b(k.list)
model.df5b <- as.data.frame(model.list5b)
model.df5b <- model.df5b %>% add_rownames()
print(model.df5b)#> # A tibble: 9 × 2
#> rowname model.list5b
#> <chr> <dbl>
#> 1 1 0.0531
#> 2 2 0.0675
#> 3 3 0.0822
#> 4 4 0.0917
#> 5 5 0.0955
#> 6 6 0.0909
#> 7 7 0.111
#> 8 8 0.111
#> 9 9 0.103
max(model.list1b)#> [1] 0.1052652
max(model.list2b) #> [1] 0.1026536
max(model.list3b)#> [1] 0.117418
max(model.list4b)#> [1] 0.09479284
max(model.list5b)#> [1] 0.1110742
Terlihat bahwa Model 3b dengan alpha = 0.6, beta = 0.05 dengan k = 7 merupakan model dengan nilai rata-rata coherence tertinggi (0.117) setelah dilakukan penghapusan kata-kata positif. Meskipun terdapat penurunan nilai coherence dibandingkan model sebelumnya (model tanpa penghapusan kata-kata positif), kita harus melihat dulu bagaimana sebaran kata-kata di setiap topiknya.
set.seed(123)
model_3b_best <- FitLdaModel(dtm = dtm_lda_neg2,
k = 7,
alpha = 0.6,
beta = 0.05,
iterations = 2000,
burnin = 1000,
calc_coherence = T)
mean(model_3b_best$coherence)#> [1] 0.117418
top_topic_model3b <- GetTopTerms(model_3b_best$phi, 30) %>%
as.data.frame() %>%
set_names(paste("Topic", 1:7))
top_topic_model3bhead(top_topic_model3b, 10) %>%
rownames_to_column("id") %>%
mutate(id = as.numeric(id)) %>%
pivot_longer(-id, names_to = "topic", values_to = "term")%>%
ggplot(aes(label = term, size = rev(id), color = topic, alpha = rev(id))) +
geom_text_wordcloud(seed = 123) +
facet_wrap(~topic, scales = "free") +
scale_alpha_continuous(range = c(0.4, 1)) +
theme_minimal() +
theme(strip.background = element_rect(fill = "black"),
strip.text.x = element_text(colour = "white"))Model yang terbentuk sudah menunjukkan interpretabilitas yang baik, di mana setiap topik memiliki kata-kata yang mudah diinterpretasikan. Namun, terdapat kesamaan isu dibeberapa topik, contohnya pada topik 2 & topik 5 sama-sama terkait isu registrasi & login, serta topik 4 & topik 7 dalam hal pembahasan yang berkaitan dengan biaya layanan. Pada saat implementasi, nantinya kita akan menggabungkan topik yang memiliki kesamaan ini menjadi satu topik.
Berikut adalah interpretasi topik yang dihasilkan oleh model :
Setelah kita menemukan model yang cocok dan memenuhi ekspektasi, kita dapat mengimplementasikan model tersebut untuk mengkategorikan data ulasan dengan setimen negatif ke dalam kelompok topik yang sesuai. Hal ini dapat dilakukan dengan merujuk pada nilai theta tertinggi. Nilai theta merupakan probabilitas terbesar untuk setiap dokumen terkait dengan masing-masing topik.
! Scroll ke samping untuk melihat kolom yang ada
data_neg_fix# Melakukan prediksi topik dengan model LDA
set.seed(123)
predicted_topics <- predict(model_3b_best, newdata = dtm_lda_neg2, iterations = 2000)
# Membuat data frame hasil klasifikasi
result <- data.frame(Ulasan = data_neg_fix$content_real, Topik = predicted_topics, rating = data_neg_fix$score, date = data_neg_fix$review_date, Clean_text = data_neg_fix$content2)
# Tampilkan hasil klasifikasi
head(result)Hasil prediksi topik diambil dari nilai probabilitas paling tinggi.
# Select the relevant columns
topic_columns <- result[, paste0("Topik.t_", 1:7)]
# Create a new column indicating the topic with the highest value
result$Topik <- colnames(topic_columns)[max.col(topic_columns)]resultKelompok Topik : - Topik 1 : Kendala Transaksi - Topik 2 & Topik 5 : Kendala Akun - Topik 3 : Kendala Cetak Struk & Bug - Topik 2 & Topik 5 : Kendala Akun - Topik 4 & Topik 7 : Isu Layanan Berbayar & User Experience - Topik 6 : Isu Data & Force Close
result$Topik <- ifelse(result$Topik == "Topik.t_1", "Kendala Transaksi",
ifelse(result$Topik %in% c("Topik.t_2", "Topik.t_5"), "Kendala Akun",
ifelse(result$Topik == "Topik.t_3", "Kendala Cetak Struk & Bug",
ifelse(result$Topik %in% c("Topik.t_4", "Topik.t_7"), "Isu Layanan Berbayar & User Experience",
ifelse(result$Topik == "Topik.t_6", "Isu Data & Force Close",
"unknown")))))Kita juga akan melihat bagaimana distribusi topik di setiap tahunnya, jadi kita akan mengekstrak tahun dari kolom date
result$year <- year(result$date)
glimpse(result)#> Rows: 424
#> Columns: 13
#> $ Ulasan <chr> "Akun dinonaktifkan peeh", "Mengapa akun saiia dinon aktifk…
#> $ Topik.t_1 <dbl> 0.09677419, 0.08333333, 0.08333333, 0.05357143, 0.07317073,…
#> $ Topik.t_2 <dbl> 0.41935484, 0.50000000, 0.36111111, 0.41071429, 0.07317073,…
#> $ Topik.t_3 <dbl> 0.09677419, 0.08333333, 0.08333333, 0.05357143, 0.07317073,…
#> $ Topik.t_4 <dbl> 0.09677419, 0.08333333, 0.08333333, 0.14285714, 0.07317073,…
#> $ Topik.t_5 <dbl> 0.09677419, 0.08333333, 0.22222222, 0.05357143, 0.56097561,…
#> $ Topik.t_6 <dbl> 0.09677419, 0.08333333, 0.08333333, 0.05357143, 0.07317073,…
#> $ Topik.t_7 <dbl> 0.09677419, 0.08333333, 0.08333333, 0.23214286, 0.07317073,…
#> $ rating <int> 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 2,…
#> $ date <date> 2023-06-20, 2023-06-08, 2023-06-04, 2023-05-26, 2023-05-22…
#> $ Clean_text <chr> "akun nonaktif", "akun nonaktif masuk", "coba daftar sandi"…
#> $ Topik <chr> "Kendala Akun", "Kendala Akun", "Kendala Akun", "Kendala Ak…
#> $ year <dbl> 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023,…
result <- result %>% select(c(Ulasan, Topik, rating, date, year, Clean_text))
head(result)Setelah selesai membangun model dan mencoba mengkategorikan setiap ulasan berdasarkan topik-topiknya, sekarang kita akan menganalisis setiap topik yang ada.
unique(result$Topik)#> [1] "Kendala Akun"
#> [2] "Isu Layanan Berbayar & User Experience"
#> [3] "Kendala Transaksi"
#> [4] "Isu Data & Force Close"
#> [5] "Kendala Cetak Struk & Bug"
topic_year <- result %>% group_by(year, Topik) %>%
summarise(Total = n()) %>% ungroup() %>%
group_by(year) %>%
mutate(peryear = sum(Total),
label = glue(
"Topic : {Topik}
Total: {round((Total/peryear)*100, 2)} %"))topic_plot <- ggplot(topic_year, aes(x = year, y = Total)) +
geom_line(aes(group = Topik, col= Topik)) +
geom_point(aes(col= Topik, text = label)) +
theme_minimal() +
labs(x = "Tahun",
y = "Total",
title = "Sebaran Isu Di Setiap Tahunnya")
ggplotly(topic_plot, tooltip = "text")💡 Insight :
Isu Akun mendominasi pada data dengan sentimen negatif di tahun 2023. Isu ini menunjukkan bahwa penggunaan platform KasirPintar seringkali menghadapi masalah saat melakukan proses registrasi atau login. Mari kita cek detail terkait ulasan dengan isu kendala registrasi & login ini untuk melihat lebih jelas keluhan pengguna.
kendala_akun <- result %>% filter(Topik == "Kendala Akun") %>% select(Ulasan, Clean_text)
kendala_akun %>% select(Ulasan)Isu Kendala Akun yang dialami pengguna Kasir Pintar mencakup :
Ketika pengguna mengalami kendala ini, mereka mungkin merasa terhalang dalam memulai atau mengelola bisnis mereka menggunakan platform Kasir Pintar. Misalnya, ketika akun pengguna dinonaktifkan, mereka kehilangan akses ke data dan fitur-fitur penting yang dibutuhkan untuk menjalankan bisnis mereka. Masalah saat melakukan verifikasi OTP/email juga dapat menyebabkan pengguna tidak dapat mengaktifkan akun mereka dengan cepat dan mulai menggunakan platform.
⚙️ Recommendation : Kasir Pintar harus meningkatkan sistem registrasi dan login pengguna. Proses verifikasi harus dirancang dengan baik dan dapat harus dipastikan diakses dengan mudah.
layanan_ux <- result %>% filter(Topik == "Isu Layanan Berbayar & User Experience") %>% select(Ulasan, Clean_text)
layanan_ux %>% select(Ulasan)Isu Layanan Berbayar & User Experience yang dialami pengguna Kasir Pintar mencakup :
⚙️ Recommendation : Kasir Pintar harus meningkatkan kualitas layanan berbayar yang diberikan kepada pengguna. Hal ini bisa dilakukan dengan memastikan bahwa value yang diberikan sebanding dengan biaya yang dikenakan kepada pengguna. Selain itu, perbaikan signifikan pada segi pengalaman pengguna perlu dilakukan, termasuk meningkatkan kecepatan dan responsivitas aplikasi serta memperbaiki tampilan dan antarmuka pengguna agar lebih user-friendly.
transaksi <- result %>% filter(Topik == "Kendala Transaksi") %>% select(Ulasan, Clean_text)
transaksi %>% select(Ulasan)Isu Kendala Transaksi yang dialami pengguna Kasir Pintar mencakup :
⚙️ Recommendation : Diperlukan pembaruan pada sistem Kasir Pintar untuk memperbaiki fungsi transaksi yang tidak berjalan dengan baik dan mengatasi kendala error yang dikeluhkan pengguna saat bertransaksi. Setiap masalah teknis yang dihadapi pengguna perlu ditangani dengan cepat dan efektif oleh tim pengembang. Mereka harus memastikan bahwa transaksi, input data, dan pembaruan stok dapat berjalan tanpa kendala yang mengganggu pengguna. Selain itu, penting juga bagi Kasir Pintar untuk memperbaiki dan meningkatkan kualitas laporan transaksi yang dihasilkan. Laporan haruslah akurat, lengkap, dan mudah dipahami.
data_force_close <- result %>% filter(Topik == "Isu Data & Force Close") %>% select(Ulasan, Clean_text)
data_force_close %>% select(Ulasan)Isu Data & Force Close yang dialami pengguna Kasir Pintar mencakup :
⚙️ Recommendation : Untuk mengatasi isu ini, disarankan kepada pengembang untuk meningkatkan stabilitas dan mengatasi masalah force close yang sering terjadi, fleksibelitas akses terhadap data, memberikan opsi pengaturan watermark. Dukungan pelanggan yang responsif juga penting untuk membantu pengguna mengatasi masalah dengan cepat.
cetak_struk <- result %>% filter(Topik == "Kendala Cetak Struk & Bug") %>% select(Ulasan, Clean_text)
cetak_struk %>% select(Ulasan)Kendala Cetak Struk & Bug Aplikasi yang dialami pengguna Kasir Pintar mencakup :
⚙️ Recommendation : Untuk mengatasi isuini, disarankan agar Kasir Pintar melakukan pemeriksaan dan perbaikan rutin terhadap bug yang ada, memperluas kompatibilitas dengan berbagai perangkat printer Bluetooth, serta melakukan pengujian yang lebih komprehensif sebelum melakukan pembaruan versi aplikasi. Dengan demikian, pengalaman pengguna dalam hal cetak struk dan stabilitas aplikasi dapat ditingkatkan.
# Load the data and perform text processing
data_neg_fix %>%
unnest_tokens(word, content2) %>%
count(word, sort = TRUE) %>%
top_n(10) %>%
ggplot(aes(x = reorder(word, n), y = n)) +
geom_col(fill = "steelblue") +
labs(x = "Words", y = "Frequency", title = "Top 10 Most Frequent Words") +
coord_flip() +
theme_minimal()Berdasarkan keseluruhan proses analisis yang telah dilakukan, berikut adalah ringkasan utama (urgensi) yang didapatkan untuk pengembangan aplikasi Kasir Pintar :