Bagian ini menyajikan gambaran umum serta sistematika pembahasan guna memudahkan pembaca dalam memahami seluruh rangkaian analisis data ini.
Kemajuan internet dan platform kerja daring membuat semakin banyak orang memilih menjadi pekerja lepas (freelancer). Fleksibilitas waktu dan tempat kerja menjadi daya tarik utama bagi para pencari kerja saat ini. Oleh karena itu, freelancer kini tidak lagi dipandang sebagai pekerjaan sampingan, melainkan sebuah pilihan utama yang menjanjikan di era digital.
Pertumbuhan jumlah pekerja lepas ini terjadi secara global dan mencakup berbagai industri. Tren ini memicu persaingan yang ketat di pasar kerja internasional. Dengan masifnya pertumbuhan tersebut, analisis data mengenai profil, keahlian, dan pendapatan para freelancer menjadi sangat penting untuk dipelajari guna memahami dinamika pasar kerja modern saat ini.
Namun, data mentah yang dikumpulkan dari berbagai platform sering kali masih berantakan, tidak konsisten, atau memiliki nilai yang hilang (missing values). Oleh karena itu, tahapan data preprocessing dan eksplorasi data awal menjadi langkah krusial untuk membersihkan dan menyiapkan data, sehingga siap digunakan untuk analisis lebih lanjut.
Rumusan masalah adalah pertanyaan-pertanyaan yang ingin dijawab oleh analisis ini.
Bagaimana kondisi dan kualitas data pada dataset Global Freelancers?
Permasalahan data apa saja yang terdapat pada dataset tersebut, seperti missing value, inkonsistensi penulisan, kesalahan format, baris kembar, dan nilai yang janggal?
Bagaimana proses pembersihan dan penanganan data (penyeragaman kategori, konversi tipe data, dan imputasi) agar data menjadi rapi dan siap digunakan?
Bagaimana karakteristik data setelah pembersihan, yaitu sebaran pengalaman kerja dan tarif per jam, kenormalan data, dan kesamaan varians tarif antara perempuan dan laki-laki?
Bagaimana hubungan antara pengalaman kerja (years of experience) dan tarif per jam (hourly rate) freelancer?
Bagaimana skala dan bentuk sebaran pengalaman kerja dan tarif per jam berubah setelah dilakukan normalisasi, standardisasi, dan transformasi invers kuadrat?
Tujuan adalah “jawaban yang ingin dicapai” dari setiap rumusan masalah di atas, dengan urutan yang sama.
Mengidentifikasi kondisi dan kualitas data pada dataset freelancer.
Menemukan dan mengevaluasi permasalahan data, seperti missing value, inkonsistensi, dan kesalahan format.
Melakukan pembersihan dan transformasi data, agar data menjadi lebih rapi dan siap digunakan.
Menganalisis karakteristik data setelah pembersihan melalui grafik dan uji asumsi (normalitas dan homogenitas varians), sebagai dasar untuk analisis selanjutnya.
Menganalisis hubungan antara pengalaman kerja (years of experience) dan tarif per jam (hourly rate) freelancer.
Menyelaraskan skala dan distribusi variabel numerik melalui transformasi, lalu menyimpan dataset final dalam format CSV.
Sebelum dibersihkan, data dilihat terlebih dahulu apa adanya. Dengan begitu, kondisi awal dapat dibandingkan dengan hasil akhir setelah seluruh proses selesai. Ibarat dokter, kita “memeriksa pasien” dulu sebelum memberi obat.
Sebelum masuk ke isi data, perlu diketahui dulu asal-usulnya. Ringkasan berikut memakai kerangka 5W+1H (siapa, apa, kapan, di mana, mengapa, dan bagaimana).
| Pertanyaan | Penjelasan |
|---|---|
| Who (siapa pembuatnya) | Urvish Ahir, kontributor di platform Kaggle. |
| What (apa isinya) | Data mentah (raw dataset) profil pekerja lepas global, dengan atribut seperti usia, jenis kelamin, negara, bahasa, keahlian utama, lama pengalaman, tarif per jam, rating, status keaktifan, dan kepuasan klien. |
| When (kapan) | Diunggah ke Kaggle pada tahun 2024. |
| Where (cakupan lokasi) | Global, mencakup berbagai negara, dari platform pasar kerja lepas digital. |
| Why (mengapa ada) | Menyediakan data mentah sebagai bahan latihan pembersihan data (data cleaning), analisis data eksploratif (EDA), dan penanganan data kotor (dirty data). |
| How (cara pengumpulan) | Dibuat dari data sintetis yang dihasilkan dengan Python menggunakan pustaka (library). |
| Format | Berkas CSV di Kaggle (pada laporan ini dibaca dari berkas Excel), dengan satu baris mewakili satu freelancer. |
Interpretasi. Karena datanya sintetis (dibuat dengan program), hasil analisis pada laporan ini tidak boleh dipakai untuk menyimpulkan kondisi nyata pasar freelancer. Tujuan utamanya adalah berlatih merapikan dan menjelajahi data mentah. Sumber data: https://www.kaggle.com/datasets/urvishahir/global-freelancers-raw-dataset.
Setiap baris data mewakili satu freelancer (total 1.000 baris dan 12 variabel). Berikut fungsi setiap kolom beserta jenis dan skala datanya.
| Variabel | Fungsi | Jenis Data | Skala |
|---|---|---|---|
| freelancer_ID | ID unik freelancer | Kualitatif | Nominal |
| name | Nama freelancer | Kualitatif | Nominal |
| gender | Jenis kelamin | Kualitatif | Nominal |
| age | Usia freelancer | Kuantitatif kontinu | Rasio |
| country | Negara asal | Kualitatif | Nominal |
| language | Bahasa utama | Kualitatif | Nominal |
| primary_skill | Keahlian utama | Kualitatif | Nominal |
| years_of_experience | Lama pengalaman kerja (tahun) | Kuantitatif diskrit | Rasio |
| hourly_rate (USD) | Tarif per jam (USD) | Kuantitatif kontinu | Rasio |
| rating | Rating freelancer (0 sampai 5) | Kuantitatif kontinu | Interval |
| is_active | Status keaktifan | Kualitatif | Nominal |
| client_satisfaction | Tingkat kepuasan klien | Kuantitatif kontinu | Rasio |
Data kualitatif berupa kategori atau label (misalnya negara), sedangkan data kuantitatif berupa angka yang bisa dihitung (misalnya usia). Diskrit berarti nilainya bulat hasil menghitung (tahun pengalaman), sedangkan kontinu bisa bernilai pecahan.
Langkah pertama adalah memasukkan file Excel ke R. File Excel
disimpan di folder yang sama dengan file .Rmd, jadi R cukup
membacanya lewat nama file tanpa alamat lengkap. Kode di bawah juga
menyertakan pengaman: jika file tidak ditemukan, proses berhenti dan
muncul pesan peringatan, sehingga kesalahan mudah dikenali.
if (!requireNamespace("readxl", quietly = TRUE)) {
install.packages("readxl")
}
library(readxl)
lokasi_file <- "data ade global_freelancers_raw.xlsx"
if (!file.exists(lokasi_file)) {
stop("File Excel tidak ditemukan. Pastikan satu folder dengan file Rmd.")
}
ADE <- read_excel(lokasi_file)Interpretasi. Kode ini tidak menampilkan angka apa
pun, tetapi tidak muncul pesan galat. Artinya file berhasil dibaca dan
seluruh isinya kini tersimpan di objek bernama ADE. Objek
inilah yang akan dibersihkan langkah demi langkah pada bab-bab
berikutnya.
Setelah data masuk, kita lihat “wajah awalnya”: berapa baris dan kolomnya, apa saja nama kolomnya, tipe datanya (angka atau teks), beberapa baris pertama dan terakhir, serta ringkasan statistiknya.
## Jumlah baris: 1000
## Jumlah kolom: 12
## [1] "freelancer_ID" "name" "gender"
## [4] "age" "country" "language"
## [7] "primary_skill" "years_of_experience" "hourly_rate (USD)"
## [10] "rating" "is_active" "client_satisfaction"
## tibble [1,000 × 12] (S3: tbl_df/tbl/data.frame)
## $ freelancer_ID : chr [1:1000] "FL250001" "FL250002" "FL250003" "FL250004" ...
## $ name : chr [1:1000] "Ms. Nicole Kidd" "Vanessa Garcia" "Juan Nelson" "Amanda Spencer" ...
## $ gender : chr [1:1000] "f" "FEMALE" "male" "F" ...
## $ age : chr [1:1000] "52.0" "52.0" "53.0" "38.0" ...
## $ country : chr [1:1000] "Italy" "Australia" "Germany" "Australia" ...
## $ language : chr [1:1000] "Italian" "English" "German" "English" ...
## $ primary_skill : chr [1:1000] "Blockchain Development" "Mobile Apps" "Graphic Design" "Web Development" ...
## $ years_of_experience: chr [1:1000] "11.0" "34.0" "31.0" "4.0" ...
## $ hourly_rate (USD) : chr [1:1000] "100" "USD 100" "50" "$40" ...
## $ rating : chr [1:1000] NA "3.3" "0.0" "1.5" ...
## $ is_active : chr [1:1000] "0" "1" "N" "N" ...
## $ client_satisfaction: num [1:1000] NA 0.84 0.71 0.9 0.83 NA 0.94 0.94 0.76 NA ...
## # A tibble: 6 × 12
## freelancer_ID name gender age country language primary_skill
## <chr> <chr> <chr> <chr> <chr> <chr> <chr>
## 1 FL250001 Ms. Nicole Kidd f 52.0 Italy Italian Blockchain De…
## 2 FL250002 Vanessa Garcia FEMALE 52.0 Australia English Mobile Apps
## 3 FL250003 Juan Nelson male 53.0 Germany German Graphic Design
## 4 FL250004 Amanda Spencer F 38.0 Australia English Web Developme…
## 5 FL250005 Lynn Curtis DDS female 53.0 Germany German Web Developme…
## 6 FL250006 Lisa Johnson female 59.0 Netherlands Dutch AI
## # ℹ 5 more variables: years_of_experience <chr>, `hourly_rate (USD)` <chr>,
## # rating <chr>, is_active <chr>, client_satisfaction <dbl>
## # A tibble: 6 × 12
## freelancer_ID name gender age country language primary_skill
## <chr> <chr> <chr> <chr> <chr> <chr> <chr>
## 1 FL250995 Jennifer Hinton Female 34.0 South Africa Afrikaa… DevOps
## 2 FL250996 Albert Wilcox Male 56.0 Turkey Turkish DevOps
## 3 FL250997 Cheryl Norris f 26.0 Germany German Blockchain D…
## 4 FL250998 Kathy Watkins female 37.0 Japan Japanese Data Analysis
## 5 FL250999 John Obrien m 46.0 Russia Russian Machine Lear…
## 6 FL251000 Dawn Green Female 36.0 Mexico Spanish UI/UX Design
## # ℹ 5 more variables: years_of_experience <chr>, `hourly_rate (USD)` <chr>,
## # rating <chr>, is_active <chr>, client_satisfaction <dbl>
## freelancer_ID name gender age
## Length:1000 Length:1000 Length:1000 Length:1000
## Class :character Class :character Class :character Class :character
## Mode :character Mode :character Mode :character Mode :character
##
##
##
##
## country language primary_skill years_of_experience
## Length:1000 Length:1000 Length:1000 Length:1000
## Class :character Class :character Class :character Class :character
## Mode :character Mode :character Mode :character Mode :character
##
##
##
##
## hourly_rate (USD) rating is_active client_satisfaction
## Length:1000 Length:1000 Length:1000 Min. : 0.60
## Class :character Class :character Class :character 1st Qu.: 0.71
## Mode :character Mode :character Mode :character Median : 0.82
## Mean : 11.73
## 3rd Qu.: 0.94
## Max. :100.00
## NA's :176
Interpretasi. Dataset berisi 1.000
baris (1.000 freelancer) dan 12 kolom:
freelancer_ID, name, gender,
age, country, language,
primary_skill, years_of_experience,
hourly_rate (USD), rating,
is_active, dan client_satisfaction. Hal
penting yang terlihat dari str() adalah kolom
hourly_rate (USD) dan is_active masih terbaca
sebagai teks, padahal seharusnya tarif berupa angka.
Penyebabnya, isinya campuran angka dan simbol seperti $ dan
USD. Selama masih berupa teks, tarif tidak bisa dihitung
rata-ratanya. Ini menjadi tanda pertama bahwa data perlu
dibersihkan.
Bab ini adalah tahap “mendiagnosis dan mengobati”. Setiap subbab menangani satu jenis masalah: sel kosong, penulisan yang tidak seragam, simbol yang menempel pada angka, baris ganda, dan angka yang tidak masuk akal. Di setiap subbab, ada penjelasan masalahnya, kodenya, lalu interpretasi hasilnya.
Missing value adalah sel yang kosong (tidak ada isinya). Sel kosong perlu dihitung dulu karena sebagian besar fungsi statistik tidak bisa bekerja dengan benar jika ada sel kosong. Kita hitung jumlah dan persentasenya di tiap kolom, supaya tahu kolom mana yang paling bermasalah.
## freelancer_ID name gender age
## 0 0 0 30
## country language primary_skill years_of_experience
## 0 0 0 51
## hourly_rate (USD) rating is_active client_satisfaction
## 94 101 89 176
## freelancer_ID name gender age
## 0.0 0.0 0.0 3.0
## country language primary_skill years_of_experience
## 0.0 0.0 0.0 5.1
## hourly_rate (USD) rating is_active client_satisfaction
## 9.4 10.1 8.9 17.6
## Total missing value: 541
Interpretasi. Terdapat 541 sel
kosong yang tersebar di enam kolom. Kolom dengan sel kosong
terbanyak adalah client_satisfaction (176 data, 17,6%),
disusul rating (101 data, 10,1%),
hourly_rate (USD) (94 data, 9,4%), is_active
(89 data, 8,9%), years_of_experience (51 data, 5,1%), dan
age (30 data, 3,0%). Kolom identitas, gender,
country, language, dan
primary_skill lengkap. Persentase kosong tertinggi masih di
bawah 20%, jadi datanya masih tergolong aman untuk diperbaiki. Karena
itu, baris yang kosong tidak perlu dibuang. Cukup diisi dengan nilai
pengganti (imputasi), sehingga ke-1.000 freelancer tetap bisa
dipakai.
Kolom gender ditulis dengan banyak gaya untuk arti yang
sama, misalnya F, f, female, atau
FEMALE. Bagi komputer, huruf besar dan kecil dianggap
berbeda, sehingga F dan f terhitung dua
kategori. Karena itu, semua variasi disatukan menjadi dua kategori saja:
Female dan Male. Caranya: ubah semua huruf
menjadi kecil, buang spasi di pinggir, lalu petakan ke dua kategori.
##
## f F female Female FEMALE m M male Male MALE
## 103 90 86 96 115 99 106 100 103 102
ADE$gender <- tolower(trimws(as.character(ADE$gender)))
ADE$gender[ADE$gender %in% c("f", "female")] <- "Female"
ADE$gender[ADE$gender %in% c("m", "male")] <- "Male"
table(ADE$gender, useNA = "ifany")##
## Female Male
## 490 510
Interpretasi. Sebelum diseragamkan,
gender memiliki 10 bentuk penulisan
(misalnya F, f, female,
FEMALE, M, m, male,
MALE). Setelah proses, hanya tersisa dua kategori:
Female 490 dan Male 510. Artinya,
jumlah freelancer perempuan dan laki-laki hampir sama (49% berbanding
51%), jadi data tidak timpang ke satu jenis kelamin. Tidak ada data
gender yang kosong, sehingga kolom ini sudah siap dipakai, termasuk
untuk uji homogenitas pada bab 7.
Tarif per jam ditulis dalam beberapa gaya, seperti $20,
USD 30, atau angka saja, sehingga R membacanya sebagai
teks. Agar bisa dihitung, simbol $ dan kata
USD dibuang dulu, spasi dirapikan, lalu kolom diubah
menjadi angka.
## [1] "100" "USD 100" "50" "$40" "30" "$30" "USD 75"
## [8] "USD 40" NA "$50" "40" "75" "USD 50" "USD 30"
## [15] "$20" "20" "$75" "$100" "USD 20"
ADE$`hourly_rate (USD)` <- gsub("\\$", "", as.character(ADE$`hourly_rate (USD)`))
ADE$`hourly_rate (USD)` <- gsub("USD", "", ADE$`hourly_rate (USD)`, ignore.case = TRUE)
ADE$`hourly_rate (USD)` <- trimws(ADE$`hourly_rate (USD)`)
ADE$`hourly_rate (USD)` <- as.numeric(ADE$`hourly_rate (USD)`)
str(ADE$`hourly_rate (USD)`)## num [1:1000] 100 100 50 40 30 30 75 40 NA 50 ...
Interpretasi. Kolom hourly_rate (USD)
kini bertipe numerik dengan 906 nilai valid dan 94
nilai kosong (sel kosong ini akan diisi pada subbab 4.2). Menariknya,
tarif yang muncul hanya beberapa tingkatan tetap, yaitu 20, 30,
40, 50, 75, dan 100 dolar per jam. Artinya tarif tidak bersifat
kontinu seperti tinggi badan yang bisa bernilai 160,3 atau 160,4. Pola
“anak tangga” ini akan terlihat jelas di grafik pada bab 6 dan
memengaruhi hasil uji statistik pada bab 7.
Tanda persen dihapus lalu kolom diubah menjadi angka. Pada data ini skalanya tidak seragam: sebagian nilai ditulis dalam skala 0 sampai 1 (misalnya 0,84) dan sebagian dalam skala 0 sampai 100 atau persen (misalnya 84%). Padahal artinya sama. Karena itu nilai yang lebih besar dari 1 dibagi 100 agar semuanya berada pada skala 0 sampai 1. Sel yang masih kosong lalu diisi dengan median, sebab median lebih tahan terhadap nilai yang sangat besar atau sangat kecil dibandingkan rata-rata.
## [1] NA 0.84 0.71 0.90 0.83 0.94 0.76 0.77 0.86 0.93
## [11] 0.70 0.69 0.60 0.87 0.75 0.68 0.65 1.00 92.00 0.89
## [21] 0.62 82.00 0.81 0.63 0.67 0.80 0.74 0.85 0.79 0.72
## [31] 64.00 88.00 0.96 96.00 81.00 0.61 0.97 0.64 0.73 0.88
## [41] 72.00 0.92 0.82 93.00 83.00 78.00 0.95 80.00 87.00 0.66
## [51] 0.78 68.00 0.91 97.00 60.00 70.00 0.99 76.00 86.00 95.00
## [61] 74.00 100.00 73.00 67.00 77.00 0.98 71.00 85.00 91.00 94.00
## [71] 84.00 90.00 62.00 65.00 75.00 63.00 61.00 66.00 99.00 79.00
## [81] 69.00 89.00
ADE$client_satisfaction <- gsub("%", "", as.character(ADE$client_satisfaction))
ADE$client_satisfaction <- trimws(ADE$client_satisfaction)
ADE$client_satisfaction <- as.numeric(ADE$client_satisfaction)
str(ADE$client_satisfaction)## num [1:1000] NA 0.84 0.71 0.9 0.83 NA 0.94 0.94 0.76 NA ...
# Menyamakan skala: nilai 0-100 diubah ke skala 0-1
jumlah_skala_besar <- sum(ADE$client_satisfaction > 1, na.rm = TRUE)
cat("Nilai di atas 1 (skala 0-100):", jumlah_skala_besar, "\n")## Nilai di atas 1 (skala 0-100): 114
skala_besar <- !is.na(ADE$client_satisfaction) &
ADE$client_satisfaction > 1
ADE$client_satisfaction[skala_besar] <-
ADE$client_satisfaction[skala_besar] / 100
summary(ADE$client_satisfaction)## Min. 1st Qu. Median Mean 3rd Qu. Max. NA's
## 0.6000 0.6975 0.7900 0.7927 0.8900 1.0000 176
# Isi NA dengan median
jumlah_na_satisfaction <- sum(is.na(ADE$client_satisfaction))
cat("Jumlah NA client satisfaction:", jumlah_na_satisfaction, "\n")## Jumlah NA client satisfaction: 176
median_satisfaction <- median(ADE$client_satisfaction, na.rm = TRUE)
ADE$client_satisfaction[is.na(ADE$client_satisfaction)] <- median_satisfaction
summary(ADE$client_satisfaction)## Min. 1st Qu. Median Mean 3rd Qu. Max.
## 0.6000 0.7100 0.7900 0.7922 0.8700 1.0000
## [1] 0
Interpretasi. Awalnya kolom ini memuat dua skala penulisan sekaligus. Sebanyak 114 nilai (11,4% data) ditulis pada skala 0 sampai 100 (atau persen), lalu dibagi 100 sehingga seluruh data kini berada pada skala 0 sampai 1. Setelah itu, 176 nilai kosong diisi dengan median sebesar 0,79, dan jumlah sel kosong menjadi 0. Penyamaan skala ini penting: tanpanya, rata-rata kepuasan klien akan jauh lebih besar dari yang sebenarnya karena angka 84 dan 0,84 dianggap berbeda.
Kolom usia, pengalaman kerja, dan rating tersimpan sebagai teks
(misalnya "52.0"), sehingga belum bisa dihitung. Kode di
bawah juga memeriksa akhiran .00.00 (misalnya
52.00.00) yang sering muncul pada data mentah dan tidak
dikenali sebagai angka, lalu membuangnya jika ada. Setelah itu, ketiga
kolom dikonversi menjadi angka.
## [1] "52.0" "53.0" "38.0" "59.0" "43.0" "26.0" "40.0" "55.0" "57.0" "50.0"
## [11] "42.0" "39.0" "56.0" NA "51.0" "54.0" "22.0" "34.0" "36.0" "47.0"
## [21] "41.0" "46.0" "29.0" "60.0" "21.0" "35.0" "25.0" "23.0" "31.0" "58.0"
## [31] "45.0" "33.0" "32.0" "37.0" "49.0" "27.0" "48.0" "30.0" "20.0" "24.0"
## [41] "44.0" "28.0"
cat("Nilai age berakhiran .00.00:",
sum(grepl("\\.00\\.00$", as.character(ADE$age)), na.rm = TRUE), "\n")## Nilai age berakhiran .00.00: 0
ADE$age <- gsub("\\.00\\.00$", "", as.character(ADE$age))
ADE$age <- gsub("%", "", ADE$age)
ADE$age <- trimws(ADE$age)
ADE$age <- as.numeric(ADE$age)
unique(ADE$years_of_experience)## [1] "11.0" "34.0" "31.0" "4.0" "27.0" "14.0" "10.0" "22.0" "17.0" "15.0"
## [11] "18.0" "20.0" "0.0" "13.0" NA "3.0" "9.0" "21.0" "8.0" "19.0"
## [21] "32.0" "28.0" "6.0" "23.0" "7.0" "1.0" "33.0" "16.0" "35.0" "12.0"
## [31] "38.0" "5.0" "30.0" "2.0" "25.0" "39.0" "29.0" "40.0" "26.0" "24.0"
## [41] "37.0" "41.0"
cat("Nilai years_of_experience berakhiran .00.00:",
sum(grepl("\\.00\\.00$", as.character(ADE$years_of_experience)), na.rm = TRUE), "\n")## Nilai years_of_experience berakhiran .00.00: 0
ADE$years_of_experience <- gsub("\\.00\\.00$", "", as.character(ADE$years_of_experience))
ADE$years_of_experience <- gsub("%", "", ADE$years_of_experience)
ADE$years_of_experience <- trimws(ADE$years_of_experience)
ADE$years_of_experience <- as.numeric(ADE$years_of_experience)
unique(ADE$rating)## [1] NA "3.3" "0.0" "1.5" "4.8" "2.4" "3.1" "4.6" "4.0" "3.6" "2.0" "2.9"
## [13] "3.7" "1.2" "2.5" "1.1" "1.8" "4.7" "1.3" "1.0" "4.2" "1.4" "2.2" "3.2"
## [25] "2.7" "2.8" "4.9" "4.5" "1.9" "3.4" "2.3" "3.9" "3.5" "4.4" "2.6" "1.6"
## [37] "3.8" "1.7" "5.0" "3.0" "4.1" "4.3" "2.1"
ADE$rating <- gsub("%", "", as.character(ADE$rating))
ADE$rating <- trimws(ADE$rating)
ADE$rating <- as.numeric(ADE$rating)
str(ADE[, c("age", "years_of_experience", "rating")])## tibble [1,000 × 3] (S3: tbl_df/tbl/data.frame)
## $ age : num [1:1000] 52 52 53 38 53 59 52 43 26 52 ...
## $ years_of_experience: num [1:1000] 11 34 31 4 27 14 10 14 4 22 ...
## $ rating : num [1:1000] NA 3.3 0 1.5 4.8 2.4 3.1 4.6 4 NA ...
Interpretasi. Ketiga kolom sekarang bertipe numerik,
jadi sudah bisa dihitung rata-rata, median, dan sebarannya. Pemeriksaan
akhiran .00.00 menghasilkan 0 pada kedua
kolom, artinya akhiran itu tidak ada di data ini. Kolom-kolom tersebut
hanya perlu dikonversi dari teks ke angka. Sel kosongnya masih ada,
yaitu 30 pada age, 51 pada
years_of_experience, dan 101 pada rating.
Sel-sel ini akan ditangani pada tahap imputasi (subbab 4.2).
Status keaktifan dicatat dengan banyak kode (1,
y, yes, true, 0,
n, dan lainnya). Semua kode yang berarti aktif dipetakan
menjadi Active, semua kode yang berarti tidak aktif menjadi
Inactive, sedangkan data kosong atau yang tidak dikenali
diberi label Unknown. Memberi label Unknown
lebih jujur daripada menebak status seseorang.
##
## 0 1 FALSE N no TRUE Y yes <NA>
## 182 190 97 98 88 77 94 85 89
active_raw <- tolower(trimws(as.character(ADE$is_active)))
ADE$is_active <- ifelse(
active_raw %in% c("1", "y", "yes", "true"),
"Active",
ifelse(
active_raw %in% c("0", "n", "no", "false"),
"Inactive",
NA_character_
)
)
ADE$is_active[is.na(ADE$is_active)] <- "Unknown"
table(ADE$is_active, useNA = "ifany")##
## Active Inactive Unknown
## 446 465 89
## [1] 0
Interpretasi. Ditemukan 8 variasi
penulisan untuk status yang sebenarnya hanya dua: empat untuk
aktif (1, Y, yes,
TRUE) dan empat untuk tidak aktif (0,
N, no, FALSE). Hasil akhirnya
adalah Inactive 465, Active 446, dan
Unknown 89. Jumlah freelancer aktif dan tidak aktif
hampir berimbang, sedangkan yang statusnya tidak diketahui hanya sekitar
9% dari seluruh data. Angka 89 ini sama dengan jumlah sel kosong pada
kolom is_active di subbab 3.1, artinya semua data kosong
sudah tertangani dan tidak ada informasi yang dikarang.
Baris yang persis sama lebih dari satu kali bisa membuat hasil analisis berlebih, misalnya satu freelancer terhitung dua kali. Karena itu, jumlah baris ganda dihitung dulu, lalu dihapus jika ada.
## [1] 0
## # A tibble: 0 × 12
## # ℹ 12 variables: freelancer_ID <chr>, name <chr>, gender <chr>, age <dbl>,
## # country <chr>, language <chr>, primary_skill <chr>,
## # years_of_experience <dbl>, hourly_rate (USD) <dbl>, rating <dbl>,
## # is_active <chr>, client_satisfaction <dbl>
## [1] 0
## [1] 1000 12
Interpretasi. Jumlah baris duplikat adalah 0, jadi tidak ada baris yang dihapus dan data tetap berjumlah 1.000 baris. Ini kabar baik: setiap baris benar-benar mewakili satu freelancer yang berbeda.
Noisy data adalah angka yang tidak masuk akal, misalnya usia negatif, pengalaman kerja negatif, atau rating yang melebihi 5. Angka seperti ini biasanya akibat salah ketik dan bisa merusak hasil analisis. Pemeriksaan dilakukan dengan menghitung berapa banyak nilai yang berada di luar batas wajar. Jika hasilnya 0, berarti aman. Rating bernilai 0 juga dihitung karena maknanya ambigu: bisa berarti penilaian terendah atau belum adanya penilaian.
## Min. 1st Qu. Median Mean 3rd Qu. Max. NA's
## 20.00 31.00 41.00 40.51 51.00 60.00 30
## [1] 0
## [1] 0
## Min. 1st Qu. Median Mean 3rd Qu. Max. NA's
## 0.00 3.00 9.00 11.34 17.00 41.00 51
## [1] 0
## Min. 1st Qu. Median Mean 3rd Qu. Max. NA's
## 0.000 1.400 2.600 2.513 3.800 5.000 101
## [1] 0
## [1] 0
## [1] 145
## Min. 1st Qu. Median Mean 3rd Qu. Max. NA's
## 20.00 30.00 40.00 52.46 75.00 100.00 94
## [1] 0
## Min. 1st Qu. Median Mean 3rd Qu. Max.
## 0.6000 0.7100 0.7900 0.7922 0.8700 1.0000
## [1] 0
## [1] 0
Interpretasi. Semua nilai berada pada rentang yang wajar: usia 20 sampai 60 tahun, pengalaman kerja 0 sampai 41 tahun, rating 0 sampai 5, tarif 20 sampai 100 dolar, dan kepuasan klien 0 sampai 1 (setelah skalanya disamakan pada subbab 3.4). Seluruh penghitungan nilai tidak wajar bernilai 0, jadi tidak ada data yang perlu dibuang. Terdapat 145 freelancer dengan rating 0. Nilai ini tetap dipertahankan karena masih berada dalam rentang 0 sampai 5, tetapi perlu diingat maknanya ambigu (penilaian terendah atau belum dinilai).
Setelah semua masalah penulisan dan tipe data beres, bab ini
menyiapkan salinan data yang bersih (data_clean) dan
mengisi sel kosong yang masih tersisa.
Data yang sudah diseragamkan disalin ke objek baru bernama
data_clean. Dengan cara ini, hasil penyeragaman tetap
terpisah dari proses imputasi yang dilakukan berikutnya, sehingga jika
ada yang keliru kita tidak perlu mengulang dari awal.
## tibble [1,000 × 12] (S3: tbl_df/tbl/data.frame)
## $ freelancer_ID : chr [1:1000] "FL250001" "FL250002" "FL250003" "FL250004" ...
## $ name : chr [1:1000] "Ms. Nicole Kidd" "Vanessa Garcia" "Juan Nelson" "Amanda Spencer" ...
## $ gender : chr [1:1000] "Female" "Female" "Male" "Female" ...
## $ age : num [1:1000] 52 52 53 38 53 59 52 43 26 52 ...
## $ country : chr [1:1000] "Italy" "Australia" "Germany" "Australia" ...
## $ language : chr [1:1000] "Italian" "English" "German" "English" ...
## $ primary_skill : chr [1:1000] "Blockchain Development" "Mobile Apps" "Graphic Design" "Web Development" ...
## $ years_of_experience: num [1:1000] 11 34 31 4 27 14 10 14 4 22 ...
## $ hourly_rate (USD) : num [1:1000] 100 100 50 40 30 30 75 40 NA 50 ...
## $ rating : num [1:1000] NA 3.3 0 1.5 4.8 2.4 3.1 4.6 4 NA ...
## $ is_active : chr [1:1000] "Inactive" "Active" "Inactive" "Inactive" ...
## $ client_satisfaction: num [1:1000] 0.79 0.84 0.71 0.9 0.83 0.79 0.94 0.94 0.76 0.79 ...
## [1] 1000 12
## freelancer_ID name gender age
## Length:1000 Length:1000 Length:1000 Min. :20.00
## Class :character Class :character Class :character 1st Qu.:31.00
## Mode :character Mode :character Mode :character Median :41.00
## Mean :40.51
## 3rd Qu.:51.00
## Max. :60.00
## NA's :30
## country language primary_skill years_of_experience
## Length:1000 Length:1000 Length:1000 Min. : 0.00
## Class :character Class :character Class :character 1st Qu.: 3.00
## Mode :character Mode :character Mode :character Median : 9.00
## Mean :11.34
## 3rd Qu.:17.00
## Max. :41.00
## NA's :51
## hourly_rate (USD) rating is_active client_satisfaction
## Min. : 20.00 Min. :0.000 Length:1000 Min. :0.6000
## 1st Qu.: 30.00 1st Qu.:1.400 Class :character 1st Qu.:0.7100
## Median : 40.00 Median :2.600 Mode :character Median :0.7900
## Mean : 52.46 Mean :2.513 Mean :0.7922
## 3rd Qu.: 75.00 3rd Qu.:3.800 3rd Qu.:0.8700
## Max. :100.00 Max. :5.000 Max. :1.0000
## NA's :94 NA's :101
Interpretasi. Sekarang data_clean
berisi 1.000 baris dan kolom-kolom numerik (age,
years_of_experience, hourly_rate (USD),
rating, client_satisfaction) sudah bertipe
angka, sedangkan gender dan is_active sudah
berupa kategori yang rapi. Pada summary(), sel kosong masih
tampak sebagai NA's di beberapa kolom. Sel inilah yang akan
diisi pada subbab berikutnya.
Sel kosong pada pengalaman kerja, tarif per jam, dan rating diisi
dengan nilai median masing-masing kolom. Median adalah
nilai tengah ketika data diurutkan dari kecil ke besar, sehingga tidak
mudah “tertarik” oleh nilai ekstrem seperti yang bisa terjadi pada
rata-rata. Hasil imputasi pengalaman dan tarif disimpan di kolom
sementara (_clean) supaya data asli tetap utuh sebagai
pembanding.
# Years of experience
if (any(!is.na(data_clean$years_of_experience))) {
median_exp <- median(data_clean$years_of_experience, na.rm = TRUE)
data_clean$years_of_experience_clean <- ifelse(
is.na(data_clean$years_of_experience),
median_exp,
data_clean$years_of_experience
)
} else {
data_clean$years_of_experience_clean <- data_clean$years_of_experience
}
# Hourly rate
if (any(!is.na(data_clean$`hourly_rate (USD)`))) {
median_rate <- median(data_clean$`hourly_rate (USD)`, na.rm = TRUE)
data_clean$hourly_rate_clean <- ifelse(
is.na(data_clean$`hourly_rate (USD)`),
median_rate,
data_clean$`hourly_rate (USD)`
)
} else {
data_clean$hourly_rate_clean <- data_clean$`hourly_rate (USD)`
}
# Rating
if (any(!is.na(data_clean$rating))) {
median_rating <- median(data_clean$rating, na.rm = TRUE)
data_clean$rating[is.na(data_clean$rating)] <- median_rating
}
# Cek hasil imputasi
sum(is.na(data_clean$years_of_experience_clean))## [1] 0
## [1] 0
## [1] 0
Interpretasi. Median yang dipakai untuk mengisi sel
kosong adalah 9 tahun untuk pengalaman kerja,
40 dolar untuk tarif per jam, dan 2,6
untuk rating. Tiga angka 0 pada bagian “Cek hasil imputasi” berarti
tidak ada lagi sel kosong pada ketiga kolom tersebut. Perlu diingat
bahwa imputasi median membuat banyak data bernilai sama, jadi keragaman
data sedikit menyempit. Kolom age sengaja tidak diimputasi
dan dibiarkan apa adanya (30 sel kosong), karena analisis utama laporan
ini berfokus pada pengalaman kerja dan tarif per jam.
Setelah “operasi” selesai, perlu ada “pemeriksaan ulang” untuk
memastikan semuanya benar-benar sudah beres. Pemeriksaan pada bab 3
diulang terhadap data_clean: apakah masih ada sel kosong,
baris ganda, atau angka di luar batas wajar.
## tibble [1,000 × 14] (S3: tbl_df/tbl/data.frame)
## $ freelancer_ID : chr [1:1000] "FL250001" "FL250002" "FL250003" "FL250004" ...
## $ name : chr [1:1000] "Ms. Nicole Kidd" "Vanessa Garcia" "Juan Nelson" "Amanda Spencer" ...
## $ gender : chr [1:1000] "Female" "Female" "Male" "Female" ...
## $ age : num [1:1000] 52 52 53 38 53 59 52 43 26 52 ...
## $ country : chr [1:1000] "Italy" "Australia" "Germany" "Australia" ...
## $ language : chr [1:1000] "Italian" "English" "German" "English" ...
## $ primary_skill : chr [1:1000] "Blockchain Development" "Mobile Apps" "Graphic Design" "Web Development" ...
## $ years_of_experience : num [1:1000] 11 34 31 4 27 14 10 14 4 22 ...
## $ hourly_rate (USD) : num [1:1000] 100 100 50 40 30 30 75 40 NA 50 ...
## $ rating : num [1:1000] 2.6 3.3 0 1.5 4.8 2.4 3.1 4.6 4 2.6 ...
## $ is_active : chr [1:1000] "Inactive" "Active" "Inactive" "Inactive" ...
## $ client_satisfaction : num [1:1000] 0.79 0.84 0.71 0.9 0.83 0.79 0.94 0.94 0.76 0.79 ...
## $ years_of_experience_clean: num [1:1000] 11 34 31 4 27 14 10 14 4 22 ...
## $ hourly_rate_clean : num [1:1000] 100 100 50 40 30 30 75 40 40 50 ...
## [1] 1000 14
## freelancer_ID name gender age
## Length:1000 Length:1000 Length:1000 Min. :20.00
## Class :character Class :character Class :character 1st Qu.:31.00
## Mode :character Mode :character Mode :character Median :41.00
## Mean :40.51
## 3rd Qu.:51.00
## Max. :60.00
## NA's :30
## country language primary_skill years_of_experience
## Length:1000 Length:1000 Length:1000 Min. : 0.00
## Class :character Class :character Class :character 1st Qu.: 3.00
## Mode :character Mode :character Mode :character Median : 9.00
## Mean :11.34
## 3rd Qu.:17.00
## Max. :41.00
## NA's :51
## hourly_rate (USD) rating is_active client_satisfaction
## Min. : 20.00 Min. :0.000 Length:1000 Min. :0.6000
## 1st Qu.: 30.00 1st Qu.:1.500 Class :character 1st Qu.:0.7100
## Median : 40.00 Median :2.600 Mode :character Median :0.7900
## Mean : 52.46 Mean :2.521 Mean :0.7922
## 3rd Qu.: 75.00 3rd Qu.:3.700 3rd Qu.:0.8700
## Max. :100.00 Max. :5.000 Max. :1.0000
## NA's :94
## years_of_experience_clean hourly_rate_clean
## Min. : 0.00 Min. : 20.00
## 1st Qu.: 3.00 1st Qu.: 30.00
## Median : 9.00 Median : 40.00
## Mean :11.22 Mean : 51.29
## 3rd Qu.:17.00 3rd Qu.: 75.00
## Max. :41.00 Max. :100.00
##
## freelancer_ID name gender
## 0 0 0
## age country language
## 30 0 0
## primary_skill years_of_experience hourly_rate (USD)
## 0 51 94
## rating is_active client_satisfaction
## 0 0 0
## years_of_experience_clean hourly_rate_clean
## 0 0
## freelancer_ID name gender
## 0.0 0.0 0.0
## age country language
## 3.0 0.0 0.0
## primary_skill years_of_experience hourly_rate (USD)
## 0.0 5.1 9.4
## rating is_active client_satisfaction
## 0.0 0.0 0.0
## years_of_experience_clean hourly_rate_clean
## 0.0 0.0
## Baris duplikat: 0
## Usia di luar 0-100: 0
## Pengalaman kerja negatif: 0
## Rating di luar 0-5: 0
## Hourly rate negatif: 0
cat("Client satisfaction di luar 0-1:",
sum(data_clean$client_satisfaction < 0 |
data_clean$client_satisfaction > 1, na.rm = TRUE), "\n")## Client satisfaction di luar 0-1: 0
Interpretasi. Setelah pembersihan,
data_clean berisi 1.000 baris tanpa duplikat. Pada
colSums(is.na(data_clean)), kolom rating,
client_satisfaction, dan is_active sudah tidak
memiliki sel kosong. Kolom age masih memiliki 30 sel kosong
karena memang tidak diimputasi. Kolom years_of_experience
(51) dan hourly_rate (USD) (94) masih tercatat kosong
karena hasil imputasinya disimpan di kolom terpisah, yaitu
years_of_experience_clean dan
hourly_rate_clean, yang keduanya sudah lengkap (0 sel
kosong). Semua pemeriksaan rentang menunjukkan angka 0,
yang berarti tidak ada nilai di luar batas yang wajar. Setelah imputasi,
rata-rata pengalaman kerja menjadi sekitar 11,2 tahun
dan rata-rata tarif sekitar 51,3 dolar per jam.
Kesimpulannya, data sudah cukup bersih dan siap dieksplorasi.
Pada bab ini data mulai “dipelajari”. Pengalaman kerja dikelompokkan menjadi tingkatan (encoding), dihitung statistik deskriptifnya, lalu bentuk sebaran data dilihat lewat grafik. Grafik membantu kita memahami data dengan cepat sebelum masuk ke uji statistik di bab 7.
Encoding berarti mengubah data menjadi bentuk lain yang
lebih mudah dianalisis. Di sini, lama pengalaman (angka tahun) dibagi
menjadi empat tingkat: pemula (sampai 10 tahun),
menengah (11 sampai 20 tahun), jago
(21 sampai 30 tahun), dan master (lebih dari 30 tahun).
Tiap tingkat kemudian diberi kode angka 1 sampai 4 sesuai urutannya,
sehingga komputer tahu bahwa master lebih tinggi daripada pemula. Nilai
yang tidak tersedia diberi kategori unknown dan tidak
diberi kode angka.
## [1] 41
data_clean$kategori_pengalaman <- ifelse(
is.na(data_clean$years_of_experience_clean),
"unknown",
ifelse(
data_clean$years_of_experience_clean <= 10,
"pemula",
ifelse(
data_clean$years_of_experience_clean <= 20,
"menengah",
ifelse(
data_clean$years_of_experience_clean <= 30,
"jago",
"master"
)
)
)
)
data_clean$kategori_encode <- as.numeric(
factor(
data_clean$kategori_pengalaman,
levels = c("pemula", "menengah", "jago", "master")
)
)
table(
data_clean$kategori_pengalaman,
data_clean$kategori_encode,
useNA = "ifany"
)##
## 1 2 3 4
## jago 0 0 122 0
## master 0 0 0 55
## menengah 0 252 0 0
## pemula 571 0 0 0
## num [1:1000] 2 4 4 1 3 2 1 2 1 3 ...
Interpretasi. Pengalaman kerja maksimum adalah 41
tahun, sehingga keempat kategori terisi. Sebagian besar freelancer
berada di tingkat awal: pemula 571 orang (57,1%),
menengah 252 (25,2%), jago 122
(12,2%), dan master 55 (5,5%). Semakin tinggi
tingkat pengalaman, semakin sedikit jumlah freelancernya. Tabel silang
di atas memastikan pemetaan kode sudah benar: pemula berkode 1, menengah
2, jago 3, dan master 4. Kategori unknown tidak muncul
karena pengalaman kerja sudah lengkap setelah imputasi.
Statistik deskriptif adalah ringkasan angka yang menggambarkan data: berapa banyak datanya, di mana pusatnya (mean dan median), seberapa menyebar (standar deviasi), serta nilai terkecil dan terbesarnya. Perhitungan memakai data pengalaman kerja dan tarif per jam setelah imputasi, sehingga jumlah datanya lengkap. Jika mean berbeda cukup jauh dari median, sebaran data cenderung miring.
hourly_rate_test <- na.omit(data_clean$hourly_rate_clean)
experience_test <- na.omit(data_clean$years_of_experience_clean)
ringkas <- function(x) {
c(length(x), round(mean(x), 2), median(x),
round(sd(x), 2), min(x), max(x))
}
tabel_deskriptif <- data.frame(
Statistik = c("Jumlah Data", "Mean", "Median",
"Standar Deviasi", "Minimum", "Maksimum"),
Years_of_Experience = ringkas(experience_test),
Hourly_Rate_USD = ringkas(hourly_rate_test)
)
knitr::kable(tabel_deskriptif, align = "lrr")| Statistik | Years_of_Experience | Hourly_Rate_USD |
|---|---|---|
| Jumlah Data | 1000.00 | 1000.00 |
| Mean | 11.22 | 51.29 |
| Median | 9.00 | 40.00 |
| Standar Deviasi | 9.44 | 26.26 |
| Minimum | 0.00 | 20.00 |
| Maksimum | 41.00 | 100.00 |
# Nilai-nilai untuk teks interpretasi
mean_exp_desk <- mean(experience_test)
med_exp_desk <- median(experience_test)
sd_exp_desk <- sd(experience_test)
mean_rate_desk <- mean(hourly_rate_test)
med_rate_desk <- median(hourly_rate_test)
sd_rate_desk <- sd(hourly_rate_test)Interpretasi. Kedua variabel memiliki 1000 data tanpa sel kosong. Pada pengalaman kerja, rata-rata sebesar 11,22 tahun lebih besar dibanding median 9 tahun, dengan simpangan baku 9,44 dan rentang 0 sampai 41 tahun. Mean yang lebih besar dari median menandakan sebaran miring ke kanan: ada sebagian freelancer dengan pengalaman sangat lama yang menarik rata-rata ke atas. Pada tarif per jam, rata-rata 51,29 dolar dibanding median 40 dolar, dengan simpangan baku 26,26 dan rentang 20 sampai 100 dolar. Dugaan awal tentang bentuk sebaran ini akan dilihat lewat grafik pada subbab berikutnya.
Histogram menunjukkan seberapa sering suatu nilai muncul. Sumbu mendatar adalah nilai (di sini lama pengalaman), dan tinggi batang adalah jumlah freelancer pada rentang itu. Dari bentuk batangnya, kita bisa tahu apakah data simetris (seperti lonceng) atau miring.
hist(
na.omit(data_clean$years_of_experience_clean),
main = "Histogram Pengalaman Kerja",
xlab = "Years of Experience",
col = "#D08AA5",
border = "white"
)hist(
na.omit(data_clean$kategori_encode),
main = "Histogram Kategori Pengalaman",
xlab = "Kode Kategori",
breaks = seq(0.5, 4.5, by = 1),
col = "#9D4E73",
border = "white"
)Interpretasi. Histogram pengalaman kerja miring ke kanan (right-skewed): batang tertinggi berada di pengalaman rendah dan semakin ke kanan semakin pendek. Artinya mayoritas freelancer baru berpengalaman beberapa tahun, sedangkan yang berpengalaman puluhan tahun jumlahnya sedikit. Histogram kategori memperlihatkan pola yang sama, dengan batang tertinggi pada kode 1 (pemula) dan terendah pada kode 4 (master). Data yang miring seperti ini tidak berbentuk lonceng, sehingga kita sudah bisa menduga bahwa pengalaman kerja tidak berdistribusi normal. Dugaan ini akan diperiksa pada Q-Q plot (subbab 6.4) dan uji Shapiro-Wilk (subbab 7.1).
Q-Q plot adalah grafik untuk memeriksa kenormalan data secara visual. Titik-titik data dibandingkan dengan garis lurus yang mewakili distribusi normal. Jika titik-titik menempel pada garis, data mendekati normal. Semakin menyimpang dari garis, semakin jauh data dari normal.
if (length(hourly_rate_test) >= 2) {
qqnorm(hourly_rate_test, main = "QQ Plot Hourly Rate", col = "#D08AA5", pch = 19)
qqline(hourly_rate_test, col = "#4A2545", lwd = 2)
}if (length(experience_test) >= 2) {
qqnorm(experience_test, main = "QQ Plot Years of Experience", col = "#D08AA5", pch = 19)
qqline(experience_test, col = "#4A2545", lwd = 2)
}Interpretasi. Pada kedua Q-Q plot, titik-titik tidak mengikuti garis lurus. Ujung kanan melengkung menjauh dari garis, yang menandakan ada sejumlah nilai besar yang jauh lebih tinggi dari yang diharapkan pada data normal. Pada tarif per jam, titik-titik tampak berbentuk anak tangga karena tarifnya hanya muncul pada beberapa nilai tetap (20, 30, 40, 50, 75, dan 100 dolar). Dengan demikian, secara visual pengalaman kerja dan tarif per jam tidak berdistribusi normal. Kesimpulan ini sejalan dengan bentuk histogram yang miring.
Scatter plot (diagram pencar) menampilkan setiap freelancer sebagai satu titik, dengan lama pengalaman di sumbu mendatar dan tarif per jam di sumbu tegak. Grafik ini dipakai untuk melihat apakah ada hubungan antara dua variabel. Jika titik-titik membentuk pola naik, berarti makin lama pengalaman, makin tinggi tarif. Jika titik menyebar acak tanpa pola, hubungannya lemah.
plot(
data_clean$years_of_experience_clean,
data_clean$hourly_rate_clean,
main = "Pengalaman Kerja dan Hourly Rate",
xlab = "Years of Experience",
ylab = "Hourly Rate (USD)",
pch = 19,
col = "#9D4E73"
)Interpretasi. Titik-titik pada scatter plot menyebar di seluruh area dan membentuk garis-garis mendatar, sesuai dengan tarif yang hanya bernilai 20, 30, 40, 50, 75, atau 100 dolar. Tidak tampak kecenderungan naik atau turun yang jelas. Artinya, pengalaman kerja yang lebih lama tidak otomatis diikuti tarif yang jauh lebih tinggi pada data ini. Freelancer dengan pengalaman sedikit pun ada yang memasang tarif tinggi, dan sebaliknya. Ini baru kesan dari grafik. Seberapa kuat hubungannya akan diuji dengan angka pada subbab 7.3.
Banyak uji statistik mensyaratkan hal tertentu pada data, misalnya data harus berdistribusi normal atau variansnya sama antarkelompok. Bab ini memeriksa syarat-syarat tersebut secara resmi dengan uji statistik, sebagai pelengkap pengamatan visual pada bab 6.
Uji Shapiro-Wilk menguji apakah data berdistribusi normal.
Nilai W mendekati 1 berarti data semakin mirip distribusi normal. Hasil uji ini sebaiknya dibaca bersama histogram dan Q-Q plot di atas.
if (length(hourly_rate_test) >= 3 && length(hourly_rate_test) <= 5000) {
print(shapiro.test(hourly_rate_test))
} else {
message("Shapiro-Wilk membutuhkan 3 sampai 5000 observasi.")
}##
## Shapiro-Wilk normality test
##
## data: hourly_rate_test
## W = 0.85064, p-value < 2.2e-16
if (length(experience_test) >= 3 && length(experience_test) <= 5000) {
print(shapiro.test(experience_test))
} else {
message("Shapiro-Wilk membutuhkan 3 sampai 5000 observasi.")
}##
## Shapiro-Wilk normality test
##
## data: experience_test
## W = 0.9101, p-value < 2.2e-16
Interpretasi. Kedua uji menghasilkan p-value yang sangat kecil dan jauh di bawah 0,05 (statistik W sekitar 0,85 untuk tarif per jam dan 0,9101 untuk pengalaman kerja, dengan p-value pengalaman kerja < 2,2e-16). Dengan demikian H0 ditolak: baik tarif per jam maupun pengalaman kerja tidak berdistribusi normal. Hasil ini konsisten dengan bentuk histogram yang miring dan pola Q-Q plot yang menyimpang dari garis lurus. Konsekuensinya, hubungan pengalaman kerja dan tarif per jam pada subbab 7.3 dianalisis dengan korelasi Spearman yang tidak mensyaratkan normalitas (non-parametrik), bukan korelasi Pearson.
Uji Bartlett membandingkan keragaman (varians) tarif per jam antara kelompok perempuan dan laki-laki. Tujuannya mengetahui apakah tarif satu kelompok jauh lebih beragam daripada kelompok lainnya.
data_bartlett <- data_clean[
!is.na(data_clean$hourly_rate_clean) & !is.na(data_clean$gender),
]
data_bartlett$gender <- droplevels(factor(data_bartlett$gender))
if (
length(unique(data_bartlett$gender)) >= 2 &&
all(table(data_bartlett$gender) >= 2)
) {
print(bartlett.test(hourly_rate_clean ~ gender, data = data_bartlett))
} else {
message("Data kelompok belum cukup untuk uji Bartlett.")
}##
## Bartlett test of homogeneity of variances
##
## data: hourly_rate_clean by gender
## Bartlett's K-squared = 1.4921, df = 1, p-value = 0.2219
Untuk melihat angka di balik uji ini, berikut ringkasan tarif per jam menurut gender (jumlah, rata-rata, dan simpangan baku).
ringkasan_gender <- data_bartlett %>%
group_by(gender) %>%
summarise(
Jumlah = n(),
Rata_rata = round(mean(hourly_rate_clean), 2),
SD = round(sd(hourly_rate_clean), 2),
.groups = "drop"
) %>%
rename(Gender = gender)
knitr::kable(ringkasan_gender, align = "lrrr",
caption = "Ringkasan tarif per jam menurut gender")| Gender | Jumlah | Rata_rata | SD |
|---|---|---|---|
| Female | 490 | 51.91 | 26.99 |
| Male | 510 | 50.70 | 25.55 |
# Nilai-nilai untuk teks interpretasi
rata_f <- ringkasan_gender$Rata_rata[ringkasan_gender$Gender == "Female"]
rata_m <- ringkasan_gender$Rata_rata[ringkasan_gender$Gender == "Male"]
sd_f <- ringkasan_gender$SD[ringkasan_gender$Gender == "Female"]
sd_m <- ringkasan_gender$SD[ringkasan_gender$Gender == "Male"]Interpretasi. Pengujian melibatkan 490 freelancer perempuan dan 510 laki-laki. Statistik uji Bartlett (K-squared) sebesar 1,4921 dengan df = 1, dan p-value sekitar 0,22 (lebih besar dari 0,05). Tabel ringkasan mendukung hasil ini: rata-rata tarif perempuan sebesar 51,91 dolar (simpangan baku 26,99) dan laki-laki 50,70 dolar (simpangan baku 25,55). Selisih rata-ratanya hanya sekitar 1,21 dolar dan simpangan bakunya berdekatan, artinya tarif kedua kelompok hampir sama baik dari segi besar maupun keragamannya. Karena itu H0 tidak ditolak: tidak ada bukti bahwa keragaman tarif per jam berbeda antara perempuan dan laki-laki, sehingga asumsi homogenitas varians dapat dianggap terpenuhi. Dengan kata lain, sebaran tarif perempuan dan laki-laki kurang lebih sama lebar. Namun, uji Bartlett peka terhadap data yang tidak normal, dan tarif per jam terbukti tidak normal pada subbab 7.1. Karena itu hasil ini sebaiknya dipakai dengan hati-hati dan dianggap sebagai gambaran awal.
Pertanyaan utama analisis ini: apakah freelancer yang lebih berpengalaman memasang tarif per jam yang lebih tinggi? Karena data tidak berdistribusi normal (subbab 7.1), dipakai korelasi Spearman. Metode ini membandingkan peringkat data, bukan nilai aslinya, sehingga tidak mensyaratkan normalitas.
data_korelasi <- data.frame(
pengalaman = data_clean$years_of_experience_clean,
tarif = data_clean$hourly_rate_clean
)
data_korelasi <- data_korelasi[complete.cases(data_korelasi), ]
if (nrow(data_korelasi) >= 3 &&
length(unique(data_korelasi$pengalaman)) > 1 &&
length(unique(data_korelasi$tarif)) > 1) {
hasil_spearman <- cor.test(
data_korelasi$pengalaman,
data_korelasi$tarif,
method = "spearman",
exact = FALSE
)
print(hasil_spearman)
rho <- unname(hasil_spearman$estimate)
p_spearman <- hasil_spearman$p.value
kuat_txt <- as.character(cut(
abs(rho),
breaks = c(-Inf, 0.2, 0.4, 0.6, 0.8, Inf),
labels = c("sangat lemah", "lemah", "sedang", "kuat", "sangat kuat")
))
arah_txt <- ifelse(rho > 0, "positif", "negatif")
sig_txt <- ifelse(p_spearman < 0.05,
"signifikan secara statistik",
"tidak signifikan secara statistik")
cat("\nKoefisien Spearman (rho):", round(rho, 4), "\n")
cat("P-value:", signif(p_spearman, 4), "\n")
cat("Arah hubungan:", arah_txt, "\n")
cat("Kekuatan hubungan:", kuat_txt, "\n")
} else {
message("Data tidak cukup bervariasi untuk menghitung korelasi.")
}##
## Spearman's rank correlation rho
##
## data: data_korelasi$pengalaman and data_korelasi$tarif
## S = 153640365, p-value = 0.01343
## alternative hypothesis: true rho is not equal to 0
## sample estimates:
## rho
## 0.07815689
##
##
## Koefisien Spearman (rho): 0.0782
## P-value: 0.01343
## Arah hubungan: positif
## Kekuatan hubungan: sangat lemah
Interpretasi. Koefisien Spearman (rho) bernilai 0,0782 dengan p-value 0,0134 (dihitung dari 1000 freelancer). Karena p-value lebih kecil dari 0,05, H0 ditolak: terdapat hubungan yang signifikan secara statistik antara pengalaman kerja dan tarif per jam. Arah hubungannya positif, yaitu freelancer yang lebih berpengalaman cenderung memiliki tarif sedikit lebih tinggi, dan kekuatannya tergolong sangat lemah. Hubungan yang signifikan tetapi lemah berarti pola itu memang ada, namun pengaruhnya kecil. Hal ini selaras dengan scatter plot pada subbab 6.5, yang titiknya tersebar luas tanpa pola naik yang tegas. Dengan data sebanyak 1.000 baris, hubungan yang kecil pun bisa terdeteksi signifikan, jadi signifikan tidak sama dengan kuat. Perlu diingat juga bahwa korelasi hanya menunjukkan keterkaitan, bukan sebab-akibat, dan tarif yang hanya bernilai pada enam tingkat tetap membuat banyak data kembar sehingga p-value dihitung dengan pendekatan.
Tahapan transformasi numerik bertujuan untuk menyelaraskan skala dan
distribusi variabel years_of_experience_clean dan
hourly_rate_clean (hasil imputasi pada bagian 4.2) agar
siap digunakan dalam analisis eksploratif dan pemodelan statistik
lanjutan. Ada satu masalah yang ingin diatasi: pengalaman kerja berkisar
0 sampai 41, sedangkan tarif berkisar 20 sampai 100, sehingga satuan dan
skalanya tidak sebanding. Tiga transformasi diterapkan: normalisasi
min-max, standardisasi z-score, dan transformasi invers kuadrat. Tahap
ini dilakukan sebelum dataset final disusun, sehingga kolom hasil
transformasi ikut terbawa ke dataset final pada bagian 9. Perlu diingat
bahwa tidak ada satu transformasi yang selalu terbaik untuk semua data,
jadi pilihlah transformasi sesuai kebutuhan analisis.
Normalisasi min-max mengubah setiap nilai menjadi \((x - x_{min}) / (x_{max} - x_{min})\),
sehingga seluruh data berada pada rentang 0 sampai 1. Nilai terkecil
menjadi 0, nilai terbesar menjadi 1, dan nilai lain berada di antaranya.
Kode di bawah juga membuat tema grafik (tema_plum) agar
warna grafik ggplot sesuai dengan palet laporan.
# Load library pendukung
library(dplyr)
library(ggplot2)
# Tema grafik disamakan dengan palet laporan
tema_plum <- theme_minimal(base_size = 12) +
theme(
plot.background = element_rect(fill = "#FCEFF3", color = NA),
panel.background = element_rect(fill = "#FCEFF3", color = NA),
panel.grid.major = element_line(color = "#EBCFDA"),
panel.grid.minor = element_blank(),
plot.title = element_text(color = "#4A2545", face = "bold"),
axis.title = element_text(color = "#4A2545"),
axis.text = element_text(color = "#3B2A35")
)
# 1. Pemeriksaan Rentang Data Awal
rentang_awal <- data_clean %>%
summarise(
across(
c(years_of_experience_clean, hourly_rate_clean),
list(
min = ~ min(.x, na.rm = TRUE),
max = ~ max(.x, na.rm = TRUE)
)
)
)
print(rentang_awal)## # A tibble: 1 × 4
## years_of_experience_clean_min years_of_experience_clea…¹ hourly_rate_clean_min
## <dbl> <dbl> <dbl>
## 1 0 41 20
## # ℹ abbreviated name: ¹years_of_experience_clean_max
## # ℹ 1 more variable: hourly_rate_clean_max <dbl>
# 2. Definisi Fungsi dan Penerapan Min-Max
min_max <- function(x) {
(x - min(x, na.rm = TRUE)) / (max(x, na.rm = TRUE) - min(x, na.rm = TRUE))
}
data_clean <- data_clean %>%
mutate(
exp_minmax = min_max(years_of_experience_clean),
rate_minmax = min_max(hourly_rate_clean)
)
# Output Ringkas Min-Max
cat("HASIL NORMALISASI MIN-MAX\n")## HASIL NORMALISASI MIN-MAX
## Rentang Exp Min-Max : 0 - 1
## Rentang Rate Min-Max : 0 - 1
Interpretasi. Sebelum normalisasi, pengalaman kerja berkisar 0 sampai 41 tahun dan tarif per jam 20 sampai 100 dolar, sehingga skalanya sangat berbeda. Setelah normalisasi, keduanya berada pada rentang 0 sampai 1 dan dapat dibandingkan langsung. Min-max hanya menggeser dan mengubah skala, sehingga bentuk sebaran (termasuk kemiringannya) tidak berubah. Kekurangannya, metode ini peka terhadap nilai ekstrem karena bergantung pada nilai minimum dan maksimum. Satu data yang sangat besar saja bisa menekan nilai-nilai lainnya ke area yang sempit.
Standardisasi z-score mengubah nilai menjadi \((x - \bar{x}) / s\), sehingga data berpusat di 0 dengan simpangan baku 1. Nilai z menunjukkan berapa simpangan baku suatu nilai berada di atas (positif) atau di bawah (negatif) rata-rata. Misalnya z = 2 berarti nilai itu dua simpangan baku di atas rata-rata, sedangkan z = 0 berarti tepat di rata-rata.
data_clean <- data_clean %>%
mutate(
exp_z = as.numeric(scale(years_of_experience_clean)),
rate_z = as.numeric(scale(hourly_rate_clean))
)
# Verifikasi Hasil Z-Score
cat("HASIL VERIFIKASI Z-SCORE\n")## HASIL VERIFIKASI Z-SCORE
## Rata-rata Z-Score Exp : 0
## Standar Deviasi Z Exp : 1
## Rata-rata Z-Score Rate : 0
## Standar Deviasi Z Rate : 1
Interpretasi. Rata-rata z-score pengalaman kerja dan tarif per jam masing-masing 0,0000 dan 0,0000, dengan simpangan baku 1,0000 dan 1,0000. Rata-rata yang hampir 0 dan simpangan baku yang tepat 1 menunjukkan standardisasi berhasil. Kedua variabel berpusat di 0 dengan keragaman 1, sehingga sebanding walaupun satuan aslinya berbeda (tahun dan dolar). Seperti min-max, z-score tidak mengubah bentuk sebaran, jadi data yang miring ke kanan tetap miring dan tidak otomatis menjadi normal.
Transformasi invers kuadrat, \(1 / (x + 1)^2\), dicoba untuk melihat apakah kemiringan sebaran dapat dikurangi. Angka 1 ditambahkan pada penyebut untuk menghindari pembagian dengan nol, sehingga perlu dicek lebih dulu apakah ada nilai 0. Tingkat kemiringan diukur dengan skewness: nilai mendekati 0 berarti simetris, nilai positif berarti miring ke kanan, dan nilai negatif berarti miring ke kiri.
# 1. Pengecekan Keamanan Nilai Nol
cek_nol <- data_clean %>%
summarise(
min_val = min(years_of_experience_clean, na.rm = TRUE),
jumlah_nol = sum(years_of_experience_clean == 0, na.rm = TRUE)
)
print(cek_nol)## # A tibble: 1 × 2
## min_val jumlah_nol
## <dbl> <int>
## 1 0 72
# 2. Transformasi Inverse Square (+1 Proteksi Pembagian 1/0)
data_clean <- data_clean %>%
mutate(
exp_inv_sq = 1 / ((years_of_experience_clean + 1)^2),
rate_inv_sq = 1 / ((hourly_rate_clean + 1)^2)
)
# 3. Kemiringan (skewness) sebelum dan sesudah transformasi
skewness_manual <- function(x) {
x <- x[!is.na(x)]
(sum((x - mean(x))^3) / length(x)) / (sum((x - mean(x))^2) / length(x))^(3 / 2)
}
sk_exp_sebelum <- skewness_manual(data_clean$years_of_experience_clean)
sk_exp_sesudah <- skewness_manual(data_clean$exp_inv_sq)
sk_rate_sebelum <- skewness_manual(data_clean$hourly_rate_clean)
sk_rate_sesudah <- skewness_manual(data_clean$rate_inv_sq)
tabel_skew <- data.frame(
Variabel = c("Pengalaman kerja", "Tarif per jam"),
Skewness_Sebelum = round(c(sk_exp_sebelum, sk_rate_sebelum), 3),
Skewness_Sesudah = round(c(sk_exp_sesudah, sk_rate_sesudah), 3)
)
knitr::kable(tabel_skew, align = "lrr")| Variabel | Skewness_Sebelum | Skewness_Sesudah |
|---|---|---|
| Pengalaman kerja | 0.945 | 3.008 |
| Tarif per jam | 0.761 | 1.355 |
# Visualisasi Distribusi Sebelum Transformasi Invers
ggplot(data_clean, aes(x = years_of_experience_clean)) +
geom_histogram(bins = 20, fill = "#D08AA5", color = "white") +
labs(
title = "Distribusi Pengalaman Kerja Sebelum Transformasi",
x = "Pengalaman Kerja (Tahun)",
y = "Frekuensi"
) +
tema_plum# Visualisasi Distribusi Sesudah Transformasi Invers
ggplot(data_clean, aes(x = exp_inv_sq)) +
geom_histogram(bins = 20, fill = "#9D4E73", color = "white") +
labs(
title = "Distribusi Pengalaman Kerja Sesudah Transformasi Invers Kuadrat",
x = "1 / (Pengalaman + 1)^2",
y = "Frekuensi"
) +
tema_plumInterpretasi. Pengalaman kerja memiliki 72 nilai nol (minimum 0 tahun), sehingga penambahan 1 pada penyebut memang diperlukan agar tidak terjadi pembagian dengan nol. Skewness pengalaman kerja berubah dari 0,95 menjadi 3,01, sedangkan tarif per jam dari 0,76 menjadi 1,36. Pada pengalaman kerja, nilai mutlak skewness tidak menurun, sehingga transformasi ini belum membantu menyimetriskan sebaran. Hal ini dapat dibandingkan dengan kedua histogram di atas. Dua hal perlu diperhatikan. Pertama, transformasi invers membalik urutan data: nilai asli yang kecil menjadi besar, sehingga freelancer dengan pengalaman paling sedikit memperoleh nilai transformasi tertinggi dan arah hubungan dengan variabel lain ikut terbalik. Kedua, tarif per jam hanya bernilai pada enam tingkat tetap, sehingga transformasi apa pun tidak akan menghilangkan pola anak tangganya.
Seluruh variabel hasil transformasi kemudian dikonsolidasikan dalam
data_transformasi, dan ringkasan statistiknya dievaluasi.
Tujuannya memastikan semua transformasi menghasilkan angka yang sesuai
harapan dan tidak ada sel kosong.
# 4. Konsolidasi & Evaluasi Ringkasan Statistik
# (kolom transformasi sudah dihitung pada langkah sebelumnya)
data_transformasi <- data_clean
# Ringkasan Evaluasi Terpadu (Dicetak Langsung)
evaluasi_stat <- data_transformasi %>%
summarise(
Mean_Exp_Asli = round(mean(years_of_experience_clean, na.rm = TRUE), 2),
SD_Exp_Asli = round(sd(years_of_experience_clean, na.rm = TRUE), 2),
Min_Exp_MinMax = min(exp_minmax, na.rm = TRUE),
Max_Exp_MinMax = max(exp_minmax, na.rm = TRUE),
Mean_Exp_Z = round(mean(exp_z, na.rm = TRUE), 4),
SD_Exp_Z = round(sd(exp_z, na.rm = TRUE), 4),
Mean_Rate_Asli = round(mean(hourly_rate_clean, na.rm = TRUE), 2),
SD_Rate_Asli = round(sd(hourly_rate_clean, na.rm = TRUE), 2)
)
print(evaluasi_stat)## # A tibble: 1 × 8
## Mean_Exp_Asli SD_Exp_Asli Min_Exp_MinMax Max_Exp_MinMax Mean_Exp_Z SD_Exp_Z
## <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
## 1 11.2 9.44 0 1 0 1
## # ℹ 2 more variables: Mean_Rate_Asli <dbl>, SD_Rate_Asli <dbl>
# Tabel evaluasi transformasi pengalaman kerja
tabel_evaluasi <- data.frame(
Statistik = c("Mean asli", "SD asli", "Minimum Min-Max",
"Maksimum Min-Max", "Mean Z-Score", "SD Z-Score"),
Nilai = c(evaluasi_stat$Mean_Exp_Asli, evaluasi_stat$SD_Exp_Asli,
evaluasi_stat$Min_Exp_MinMax, evaluasi_stat$Max_Exp_MinMax,
evaluasi_stat$Mean_Exp_Z, evaluasi_stat$SD_Exp_Z)
)
knitr::kable(tabel_evaluasi, digits = 4, align = "lr",
caption = "Evaluasi transformasi pengalaman kerja")| Statistik | Nilai |
|---|---|
| Mean asli | 11.22 |
| SD asli | 9.44 |
| Minimum Min-Max | 0.00 |
| Maksimum Min-Max | 1.00 |
| Mean Z-Score | 0.00 |
| SD Z-Score | 1.00 |
# Pemeriksaan sel kosong pada keenam kolom hasil transformasi
kolom_baru <- c("exp_minmax", "exp_z", "exp_inv_sq",
"rate_minmax", "rate_z", "rate_inv_sq")
colSums(is.na(data_transformasi[, kolom_baru]))## exp_minmax exp_z exp_inv_sq rate_minmax rate_z rate_inv_sq
## 0 0 0 0 0 0
Interpretasi. Berdasarkan tabel evaluasi di atas, rata-rata pengalaman kerja asli adalah 11,22 tahun dengan simpangan baku 9,44, sedangkan tarif per jam rata-rata 51,29 dolar dengan simpangan baku 26,26. Hasil min-max berada tepat pada rentang 0 sampai 1, dan hasil z-score berpusat di 0,0000 dengan simpangan baku 1,0000, sesuai harapan. Keenam kolom hasil transformasi tidak memiliki sel kosong, sehingga siap dibawa ke dataset final pada bagian 9. Sebagai panduan memilih metode: normalisasi dan standardisasi cocok untuk menyamakan skala, misalnya sebelum analisis berbasis jarak, sedangkan transformasi invers kuadrat dipakai bila tujuannya mengurangi kemiringan sebaran, dengan catatan urutan nilai menjadi terbalik dan hasilnya tidak selalu membaik. Pada data ini, skewness pengalaman kerja justru naik dari 0,95 menjadi 3,01, jadi transformasi invers kuadrat kurang cocok untuk menyimetriskan sebaran pengalaman kerja.
Bab ini merapikan hasil kerja sebelumnya menjadi satu dataset akhir yang siap dipakai: nama kolom dikembalikan seperti semula, kolom sementara dibuang, lalu hanya kolom yang diperlukan yang dipilih.
Hasil imputasi dikembalikan ke kolom aslinya, kemudian kolom
sementara _clean dihapus agar dataset tetap ringkas. Dengan
begitu, nama kolom pada data final sama dengan data awal. Kolom hasil
transformasi numerik dari bagian 8 (exp_minmax,
exp_z, exp_inv_sq, rate_minmax,
rate_z, rate_inv_sq) tetap dipertahankan.
data_final <- data_clean
data_final$years_of_experience <- data_final$years_of_experience_clean
data_final$`hourly_rate (USD)` <- data_final$hourly_rate_clean
data_final$years_of_experience_clean <- NULL
data_final$hourly_rate_clean <- NULL
str(data_final)## tibble [1,000 × 20] (S3: tbl_df/tbl/data.frame)
## $ freelancer_ID : chr [1:1000] "FL250001" "FL250002" "FL250003" "FL250004" ...
## $ name : chr [1:1000] "Ms. Nicole Kidd" "Vanessa Garcia" "Juan Nelson" "Amanda Spencer" ...
## $ gender : chr [1:1000] "Female" "Female" "Male" "Female" ...
## $ age : num [1:1000] 52 52 53 38 53 59 52 43 26 52 ...
## $ country : chr [1:1000] "Italy" "Australia" "Germany" "Australia" ...
## $ language : chr [1:1000] "Italian" "English" "German" "English" ...
## $ primary_skill : chr [1:1000] "Blockchain Development" "Mobile Apps" "Graphic Design" "Web Development" ...
## $ years_of_experience: num [1:1000] 11 34 31 4 27 14 10 14 4 22 ...
## $ hourly_rate (USD) : num [1:1000] 100 100 50 40 30 30 75 40 40 50 ...
## $ rating : num [1:1000] 2.6 3.3 0 1.5 4.8 2.4 3.1 4.6 4 2.6 ...
## $ is_active : chr [1:1000] "Inactive" "Active" "Inactive" "Inactive" ...
## $ client_satisfaction: num [1:1000] 0.79 0.84 0.71 0.9 0.83 0.79 0.94 0.94 0.76 0.79 ...
## $ kategori_pengalaman: chr [1:1000] "menengah" "master" "master" "pemula" ...
## $ kategori_encode : num [1:1000] 2 4 4 1 3 2 1 2 1 3 ...
## $ exp_minmax : num [1:1000] 0.2683 0.8293 0.7561 0.0976 0.6585 ...
## $ rate_minmax : num [1:1000] 1 1 0.375 0.25 0.125 ...
## $ exp_z : num [1:1000] -0.0234 2.4119 2.0943 -0.7646 1.6707 ...
## $ rate_z : num [1:1000] 1.8549 1.8549 -0.0491 -0.4299 -0.8107 ...
## $ exp_inv_sq : num [1:1000] 0.006944 0.000816 0.000977 0.04 0.001276 ...
## $ rate_inv_sq : num [1:1000] 0.000098 0.000098 0.000384 0.000595 0.001041 ...
## freelancer_ID name gender age
## Length:1000 Length:1000 Length:1000 Min. :20.00
## Class :character Class :character Class :character 1st Qu.:31.00
## Mode :character Mode :character Mode :character Median :41.00
## Mean :40.51
## 3rd Qu.:51.00
## Max. :60.00
## NA's :30
## country language primary_skill years_of_experience
## Length:1000 Length:1000 Length:1000 Min. : 0.00
## Class :character Class :character Class :character 1st Qu.: 3.00
## Mode :character Mode :character Mode :character Median : 9.00
## Mean :11.22
## 3rd Qu.:17.00
## Max. :41.00
##
## hourly_rate (USD) rating is_active client_satisfaction
## Min. : 20.00 Min. :0.000 Length:1000 Min. :0.6000
## 1st Qu.: 30.00 1st Qu.:1.500 Class :character 1st Qu.:0.7100
## Median : 40.00 Median :2.600 Mode :character Median :0.7900
## Mean : 51.29 Mean :2.521 Mean :0.7922
## 3rd Qu.: 75.00 3rd Qu.:3.700 3rd Qu.:0.8700
## Max. :100.00 Max. :5.000 Max. :1.0000
##
## kategori_pengalaman kategori_encode exp_minmax rate_minmax
## Length:1000 Min. :1.000 Min. :0.00000 Min. :0.0000
## Class :character 1st Qu.:1.000 1st Qu.:0.07317 1st Qu.:0.1250
## Mode :character Median :1.000 Median :0.21951 Median :0.2500
## Mean :1.661 Mean :0.27368 Mean :0.3911
## 3rd Qu.:2.000 3rd Qu.:0.41463 3rd Qu.:0.6875
## Max. :4.000 Max. :1.00000 Max. :1.0000
##
## exp_z rate_z exp_inv_sq rate_inv_sq
## Min. :-1.1881 Min. :-1.1916 Min. :0.0005669 Min. :9.803e-05
## 1st Qu.:-0.8705 1st Qu.:-0.8107 1st Qu.:0.0030864 1st Qu.:1.731e-04
## Median :-0.2352 Median :-0.4299 Median :0.0100000 Median :5.949e-04
## Mean : 0.0000 Mean : 0.0000 Mean :0.1059001 Mean :7.328e-04
## 3rd Qu.: 0.6119 3rd Qu.: 0.9029 3rd Qu.:0.0625000 3rd Qu.:1.041e-03
## Max. : 3.1531 Max. : 1.8549 Max. :1.0000000 Max. :2.268e-03
##
## [1] 1000 20
## freelancer_ID name gender age
## 0 0 0 30
## country language primary_skill years_of_experience
## 0 0 0 0
## hourly_rate (USD) rating is_active client_satisfaction
## 0 0 0 0
## kategori_pengalaman kategori_encode exp_minmax rate_minmax
## 0 0 0 0
## exp_z rate_z exp_inv_sq rate_inv_sq
## 0 0 0 0
## [1] 0
Interpretasi. data_final terdiri atas
1.000 baris tanpa duplikat dan memuat kolom hasil transformasi numerik.
Sel kosong yang tersisa hanya pada kolom age (30 data),
sedangkan kolom lain sudah lengkap. Kolom
years_of_experience dan hourly_rate (USD) kini
sudah berisi data hasil imputasi, jadi tidak lagi memiliki sel kosong
seperti pada bab 5.
Integrasi data berarti menyusun variabel-variabel yang relevan ke
dalam satu dataset yang rapi agar mudah dipakai untuk analisis dan
visualisasi. Dari data final dipilih enam belas kolom, yaitu sepuluh
kolom inti dan enam kolom hasil transformasi numerik. Fungsi
intersect() dipakai agar hanya kolom yang benar-benar
tersedia yang diambil, sehingga kode tidak error bila ada nama kolom
yang tidak cocok.
## tibble [1,000 × 20] (S3: tbl_df/tbl/data.frame)
## $ freelancer_ID : chr [1:1000] "FL250001" "FL250002" "FL250003" "FL250004" ...
## $ name : chr [1:1000] "Ms. Nicole Kidd" "Vanessa Garcia" "Juan Nelson" "Amanda Spencer" ...
## $ gender : chr [1:1000] "Female" "Female" "Male" "Female" ...
## $ age : num [1:1000] 52 52 53 38 53 59 52 43 26 52 ...
## $ country : chr [1:1000] "Italy" "Australia" "Germany" "Australia" ...
## $ language : chr [1:1000] "Italian" "English" "German" "English" ...
## $ primary_skill : chr [1:1000] "Blockchain Development" "Mobile Apps" "Graphic Design" "Web Development" ...
## $ years_of_experience: num [1:1000] 11 34 31 4 27 14 10 14 4 22 ...
## $ hourly_rate (USD) : num [1:1000] 100 100 50 40 30 30 75 40 40 50 ...
## $ rating : num [1:1000] 2.6 3.3 0 1.5 4.8 2.4 3.1 4.6 4 2.6 ...
## $ is_active : chr [1:1000] "Inactive" "Active" "Inactive" "Inactive" ...
## $ client_satisfaction: num [1:1000] 0.79 0.84 0.71 0.9 0.83 0.79 0.94 0.94 0.76 0.79 ...
## $ kategori_pengalaman: chr [1:1000] "menengah" "master" "master" "pemula" ...
## $ kategori_encode : num [1:1000] 2 4 4 1 3 2 1 2 1 3 ...
## $ exp_minmax : num [1:1000] 0.2683 0.8293 0.7561 0.0976 0.6585 ...
## $ rate_minmax : num [1:1000] 1 1 0.375 0.25 0.125 ...
## $ exp_z : num [1:1000] -0.0234 2.4119 2.0943 -0.7646 1.6707 ...
## $ rate_z : num [1:1000] 1.8549 1.8549 -0.0491 -0.4299 -0.8107 ...
## $ exp_inv_sq : num [1:1000] 0.006944 0.000816 0.000977 0.04 0.001276 ...
## $ rate_inv_sq : num [1:1000] 0.000098 0.000098 0.000384 0.000595 0.001041 ...
## [1] 1000 20
## [1] "freelancer_ID" "name" "gender"
## [4] "age" "country" "language"
## [7] "primary_skill" "years_of_experience" "hourly_rate (USD)"
## [10] "rating" "is_active" "client_satisfaction"
## [13] "kategori_pengalaman" "kategori_encode" "exp_minmax"
## [16] "rate_minmax" "exp_z" "rate_z"
## [19] "exp_inv_sq" "rate_inv_sq"
kolom_analisis <- c(
"age",
"gender",
"country",
"years_of_experience",
"kategori_pengalaman",
"kategori_encode",
"hourly_rate (USD)",
"client_satisfaction",
"rating",
"is_active",
"exp_minmax",
"exp_z",
"exp_inv_sq",
"rate_minmax",
"rate_z",
"rate_inv_sq"
)
kolom_tersedia <- intersect(kolom_analisis, names(data_integrasi))
data_analisis <- data_integrasi[, kolom_tersedia, drop = FALSE]
head(data_analisis)## # A tibble: 6 × 16
## age gender country years_of_experience kategori_pengalaman kategori_encode
## <dbl> <chr> <chr> <dbl> <chr> <dbl>
## 1 52 Female Italy 11 menengah 2
## 2 52 Female Australia 34 master 4
## 3 53 Male Germany 31 master 4
## 4 38 Female Australia 4 pemula 1
## 5 53 Female Germany 27 jago 3
## 6 59 Female Netherla… 14 menengah 2
## # ℹ 10 more variables: `hourly_rate (USD)` <dbl>, client_satisfaction <dbl>,
## # rating <dbl>, is_active <chr>, exp_minmax <dbl>, exp_z <dbl>,
## # exp_inv_sq <dbl>, rate_minmax <dbl>, rate_z <dbl>, rate_inv_sq <dbl>
## tibble [1,000 × 16] (S3: tbl_df/tbl/data.frame)
## $ age : num [1:1000] 52 52 53 38 53 59 52 43 26 52 ...
## $ gender : chr [1:1000] "Female" "Female" "Male" "Female" ...
## $ country : chr [1:1000] "Italy" "Australia" "Germany" "Australia" ...
## $ years_of_experience: num [1:1000] 11 34 31 4 27 14 10 14 4 22 ...
## $ kategori_pengalaman: chr [1:1000] "menengah" "master" "master" "pemula" ...
## $ kategori_encode : num [1:1000] 2 4 4 1 3 2 1 2 1 3 ...
## $ hourly_rate (USD) : num [1:1000] 100 100 50 40 30 30 75 40 40 50 ...
## $ client_satisfaction: num [1:1000] 0.79 0.84 0.71 0.9 0.83 0.79 0.94 0.94 0.76 0.79 ...
## $ rating : num [1:1000] 2.6 3.3 0 1.5 4.8 2.4 3.1 4.6 4 2.6 ...
## $ is_active : chr [1:1000] "Inactive" "Active" "Inactive" "Inactive" ...
## $ exp_minmax : num [1:1000] 0.2683 0.8293 0.7561 0.0976 0.6585 ...
## $ exp_z : num [1:1000] -0.0234 2.4119 2.0943 -0.7646 1.6707 ...
## $ exp_inv_sq : num [1:1000] 0.006944 0.000816 0.000977 0.04 0.001276 ...
## $ rate_minmax : num [1:1000] 1 1 0.375 0.25 0.125 ...
## $ rate_z : num [1:1000] 1.8549 1.8549 -0.0491 -0.4299 -0.8107 ...
## $ rate_inv_sq : num [1:1000] 0.000098 0.000098 0.000384 0.000595 0.001041 ...
## age gender country years_of_experience
## Min. :20.00 Length:1000 Length:1000 Min. : 0.00
## 1st Qu.:31.00 Class :character Class :character 1st Qu.: 3.00
## Median :41.00 Mode :character Mode :character Median : 9.00
## Mean :40.51 Mean :11.22
## 3rd Qu.:51.00 3rd Qu.:17.00
## Max. :60.00 Max. :41.00
## NA's :30
## kategori_pengalaman kategori_encode hourly_rate (USD) client_satisfaction
## Length:1000 Min. :1.000 Min. : 20.00 Min. :0.6000
## Class :character 1st Qu.:1.000 1st Qu.: 30.00 1st Qu.:0.7100
## Mode :character Median :1.000 Median : 40.00 Median :0.7900
## Mean :1.661 Mean : 51.29 Mean :0.7922
## 3rd Qu.:2.000 3rd Qu.: 75.00 3rd Qu.:0.8700
## Max. :4.000 Max. :100.00 Max. :1.0000
##
## rating is_active exp_minmax exp_z
## Min. :0.000 Length:1000 Min. :0.00000 Min. :-1.1881
## 1st Qu.:1.500 Class :character 1st Qu.:0.07317 1st Qu.:-0.8705
## Median :2.600 Mode :character Median :0.21951 Median :-0.2352
## Mean :2.521 Mean :0.27368 Mean : 0.0000
## 3rd Qu.:3.700 3rd Qu.:0.41463 3rd Qu.: 0.6119
## Max. :5.000 Max. :1.00000 Max. : 3.1531
##
## exp_inv_sq rate_minmax rate_z rate_inv_sq
## Min. :0.0005669 Min. :0.0000 Min. :-1.1916 Min. :9.803e-05
## 1st Qu.:0.0030864 1st Qu.:0.1250 1st Qu.:-0.8107 1st Qu.:1.731e-04
## Median :0.0100000 Median :0.2500 Median :-0.4299 Median :5.949e-04
## Mean :0.1059001 Mean :0.3911 Mean : 0.0000 Mean :7.328e-04
## 3rd Qu.:0.0625000 3rd Qu.:0.6875 3rd Qu.: 0.9029 3rd Qu.:1.041e-03
## Max. :1.0000000 Max. :1.0000 Max. : 1.8549 Max. :2.268e-03
##
Interpretasi. Dataset analisis memuat 16
kolom. Sepuluh kolom inti adalah age,
gender, country,
years_of_experience, kategori_pengalaman,
kategori_encode, hourly_rate (USD),
client_satisfaction, rating, dan
is_active, ditambah enam kolom hasil transformasi numerik:
exp_minmax, exp_z, exp_inv_sq,
rate_minmax, rate_z, dan
rate_inv_sq. Kolom identitas seperti
freelancer_ID dan name, serta
language dan primary_skill, tidak diikutkan
karena tidak dipakai pada analisis di laporan ini.
Sebagai pemeriksaan terakhir, dataset hasil integrasi dicek ulang: ukurannya, sel kosong, baris ganda, serta sebaran rating, status keaktifan, dan kategori pengalaman. Tujuannya memastikan tidak ada yang berubah atau hilang saat kolom dipilih.
## [1] 1000 16
## age gender country years_of_experience
## 30 0 0 0
## kategori_pengalaman kategori_encode hourly_rate (USD) client_satisfaction
## 0 0 0 0
## rating is_active exp_minmax exp_z
## 0 0 0 0
## exp_inv_sq rate_minmax rate_z rate_inv_sq
## 0 0 0 0
## [1] 0
## Min. 1st Qu. Median Mean 3rd Qu. Max.
## 0.000 1.500 2.600 2.521 3.700 5.000
## [1] 0
##
## Active Inactive Unknown
## 446 465 89
##
## jago master menengah pemula
## 122 55 252 571
Interpretasi. Dimensi dataset analisis adalah
1.000 baris x 16 kolom tanpa duplikat. Satu-satunya
kolom yang masih memiliki sel kosong adalah age (30 data),
sedangkan keenam kolom transformasi lengkap. Rating tidak lagi memiliki
sel kosong, status keaktifan terdiri atas Inactive, Active, dan Unknown,
dan keempat kategori pengalaman terisi dengan jumlah yang sama seperti
hasil encoding sebelumnya. Artinya, dataset ini konsisten dengan hasil
bab-bab sebelumnya dan siap disimpan.
Tahap terakhir pengolahan adalah menyimpan dataset ke file CSV agar
bisa dibuka kembali tanpa mengulang seluruh proses. Dataset yang
disimpan adalah data_analisis hasil integrasi pada bagian
9.2, yaitu 16 kolom yang terdiri atas 10 kolom inti dan 6 kolom hasil
transformasi numerik (bagian 8).
write.csv(
data_analisis,
"data_analisis_final.csv",
row.names = FALSE
)
message("Proses selesai. Dataset final telah disimpan.")Interpretasi. Pesan “Proses selesai” menandakan file
berhasil dibuat. File data_analisis_final.csv tersimpan di
folder yang sama dengan file Rmd dan bisa langsung dipakai untuk
analisis lanjutan, misalnya korelasi, regresi, atau visualisasi
lain.
Berikut jawaban atas enam rumusan masalah pada bab 1, dengan urutan yang sama.
$, USD), skala kepuasan
klien yang tercampur (0 sampai 1 dan 0 sampai 100), serta kolom usia,
pengalaman kerja, dan rating yang tersimpan sebagai teks. Tidak
ditemukan baris ganda maupun angka di luar rentang yang wajar.client_satisfaction disamakan ke 0 sampai 1, dan
is_active menjadi Active, Inactive, atau Unknown. Sel
kosong diisi median (pengalaman 9 tahun, tarif 40 dolar, rating 2,6, dan
kepuasan klien 0,79), sedangkan age dibiarkan apa adanya
(30 data kosong).Interpretasi Akhir. Data mentah yang awalnya
berantakan (sel kosong, penulisan tidak seragam, dan simbol yang
bercampur dengan angka) kini sudah menjadi dataset bersih berukuran
1000 baris x 16 kolom dan disimpan sebagai
data_analisis_final.csv. Pada dataset tersebut, pengalaman
kerja dan tarif per jam memiliki hubungan positif yang
signifikan secara statistik, dengan kekuatan
sangat lemah. Artinya, freelancer yang lebih
berpengalaman tidak otomatis memasang tarif yang jauh lebih tinggi.
Pengalaman kerja hanyalah salah satu dari banyak faktor yang kemungkinan
memengaruhi tarif.
Beberapa hal perlu diingat saat membaca hasil laporan ini.
age
masih memiliki 30 sel kosong.Untuk analisis berikutnya, beberapa langkah yang bisa dicoba:
primary_skill), negara (country), dan rating
untuk mencari faktor yang lebih berperan terhadap tarif.Penutup.
Data yang bersih adalah fondasi analisis yang bisa dipercaya. Dengan tahapan preprocessing yang rapi, data freelancer ini sudah siap dipakai untuk menjawab pertanyaan yang lebih dalam tentang apa saja yang menentukan tarif seorang freelancer.