Pasar kerja lepas global kini berkembang pesat seiring meluasnya platform daring yang mempertemukan penyedia jasa dengan klien lintas negara. Data profil pekerja pada platform semacam itu berguna untuk mengetahui siapa saja yang terlibat, seberapa berpengalaman mereka, berapa tarif yang ditetapkan, serta bagaimana klien menilai hasil kerjanya.
Namun, data yang dihimpun dari berbagai sumber jarang langsung dalam
keadaan rapi. Global Freelancers Dataset dari Kaggle yang
digunakan dalam laporan ini, dengan 1.000 baris data dan 12 variabel,
masih tergolong data mentah. Di dalamnya dijumpai nilai kosong,
penulisan kategori yang berbeda-beda untuk makna yang sama, dan angka
yang tercampur simbol seperti $, USD, dan
%. Selain itu, masih perlu dipastikan apakah terdapat baris
ganda dan nilai yang tidak wajar.
Analisis yang dilakukan di atas data seperti itu dapat menghasilkan kesimpulan yang keliru. Oleh karena itu, data terlebih dahulu dibenahi melalui tahapan data wrangling, yaitu pembersihan, transformasi, dan integrasi, hingga terbentuk dataset yang konsisten. Dataset tersebut kemudian digunakan untuk menelaah karakteristik pengalaman kerja dan tarif per jam, memeriksa asumsi statistik, serta melihat keterkaitan antara keduanya.
Data diperoleh dari Kaggle dengan nama Global Freelancers Dataset yang diunggah oleh Urvish Ahir (https://www.kaggle.com/datasets/urvishahir/global-freelancers-raw-dataset). Setiap baris mewakili satu freelancer, dengan 1.000 baris data dan 12 variabel sebagai berikut.
| Variabel | Keterangan | 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-5) | Kuantitatif kontinu | Interval |
| is_active | Status keaktifan | Kualitatif | Nominal |
| client_satisfaction | Tingkat kepuasan klien | Kuantitatif kontinu | Rasio |
Tahap pertama adalah memuat file Excel ke dalam R. File diletakkan satu folder dengan dokumen ini, sehingga lokasinya cukup ditulis dengan nama file. Apabila file tidak ditemukan, proses dihentikan dan muncul pesan peringatan.
Setelah data berhasil dimuat, struktur awalnya diperiksa, meliputi jumlah baris dan kolom, nama variabel, tipe data, dan ringkasan statistik. Keluaran ini menggambarkan kondisi data mentah sebelum diolah, termasuk variabel yang masih terbaca sebagai teks.
## 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
## N.unique :1000 N.unique : 992 N.unique : 10 N.unique : 41
## N.blank : 0 N.blank : 0 N.blank : 0 N.blank : 0
## Min.nchar: 8 Min.nchar: 7 Min.nchar: 1 Min.nchar: 4
## Max.nchar: 8 Max.nchar: 25 Max.nchar: 6 Max.nchar: 4
## NAs : 30
##
## country language primary_skill years_of_experience
## Length :1000 Length :1000 Length :1000 Length :1000
## N.unique : 21 N.unique : 16 N.unique : 10 N.unique : 41
## N.blank : 0 N.blank : 0 N.blank : 0 N.blank : 0
## Min.nchar: 5 Min.nchar: 5 Min.nchar: 2 Min.nchar: 3
## Max.nchar: 14 Max.nchar: 10 Max.nchar: 22 Max.nchar: 4
## NAs : 51
##
## hourly_rate (USD) rating is_active client_satisfaction
## Length :1000 Length :1000 Length :1000 Min. : 0.60
## N.unique : 18 N.unique : 42 N.unique : 8 1st Qu.: 0.71
## N.blank : 0 N.blank : 0 N.blank : 0 Median : 0.82
## Min.nchar: 2 Min.nchar: 3 Min.nchar: 1 Mean : 11.73
## Max.nchar: 7 Max.nchar: 3 Max.nchar: 5 3rd Qu.: 0.94
## NAs : 94 NAs : 101 NAs : 89 Max. :100.00
## NAs :176
Nilai hilang dihitung pada setiap variabel, baik dalam jumlah maupun persentase. Variabel dengan persentase nilai hilang yang besar perlu mendapat perhatian lebih pada tahap penanganan.
## 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
Variabel gender ditulis dengan beragam bentuk untuk
kategori yang sama, misalnya f, F, dan
female. Penulisan tersebut diseragamkan menjadi
Female dan Male agar jumlah pada setiap
kelompok dapat dihitung dengan benar.
##
## 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
Tarif per jam masih bercampur dengan simbol $ dan
tulisan USD, sehingga terbaca sebagai teks dan tidak dapat
dihitung. Simbol tersebut dihapus, kemudian variabel 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 ...
Tanda persen dibuang lalu kolom dijadikan numerik. Pada data ini skalanya tidak seragam: sebagian nilai ditulis dalam skala 0 sampai 1 (misalnya 0,84) dan sebagian dalam skala 0 sampai 100 (misalnya 84). Nilai di atas 1 karena itu dibagi 100 agar semuanya berada pada skala 0 sampai 1. Nilai kosong yang tersisa diisi dengan median, karena median tidak mudah terpengaruh oleh nilai ekstrem.
## [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
cat("Nilai di atas 1 (skala 0-100):",
sum(ADE$client_satisfaction > 1, na.rm = TRUE), "\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. NAs
## 0.6000 0.6975 0.7900 0.7927 0.8900 1.0000 176
## [1] 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
Variabel usia, pengalaman kerja, dan rating masih terbaca sebagai teks sehingga perlu diubah menjadi angka. Langkah wrangling yang sama diterapkan pada ketiganya, yaitu membuang tanda yang tidak diperlukan dan mengonversi tipe datanya.
## [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"
ADE$age <- gsub("%", "", as.character(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"
ADE$years_of_experience <- gsub("%", "", as.character(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 ...
Status keaktifan ditulis dalam banyak bentuk (1,
Y, yes, True, dan sejenisnya).
Seluruhnya disatukan menjadi Active atau
Inactive, sedangkan nilai yang tidak dapat dikenali diberi
label Unknown.
##
## 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
Baris yang sama persis lebih dari satu kali dapat membuat hasil analisis menjadi berlebih. Karena itu, jumlah baris ganda dihitung terlebih dahulu, kemudian dihapus apabila 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
Pemeriksaan ini memastikan setiap nilai berada pada rentang yang masuk akal, misalnya usia tidak negatif dan rating tidak melebihi 5. Rating bernilai 0 juga dicatat karena maknanya ambigu: dapat berarti penilaian terendah atau belum adanya penilaian. Nilai tersebut tetap dipertahankan karena masih berada dalam rentang 0 sampai 5.
## Min. 1st Qu. Median Mean 3rd Qu. Max. NAs
## 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. NAs
## 0.00 3.00 9.00 11.34 17.00 41.00 51
## [1] 0
## Min. 1st Qu. Median Mean 3rd Qu. Max. NAs
## 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. NAs
## 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
Data yang telah diseragamkan disalin ke objek baru bernama
data_clean, sehingga data hasil wrangling tersimpan
terpisah dari tahap imputasi. Struktur dan ringkasannya ditampilkan
untuk memastikan tipe data sudah sesuai.
## 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
## N.unique :1000 N.unique : 992 N.unique : 2 1st Qu.:31.00
## N.blank : 0 N.blank : 0 N.blank : 0 Median :41.00
## Min.nchar: 8 Min.nchar: 7 Min.nchar: 4 Mean :40.51
## Max.nchar: 8 Max.nchar: 25 Max.nchar: 6 3rd Qu.:51.00
## Max. :60.00
## NAs :30
## country language primary_skill years_of_experience
## Length :1000 Length :1000 Length :1000 Min. : 0.00
## N.unique : 21 N.unique : 16 N.unique : 10 1st Qu.: 3.00
## N.blank : 0 N.blank : 0 N.blank : 0 Median : 9.00
## Min.nchar: 5 Min.nchar: 5 Min.nchar: 2 Mean :11.34
## Max.nchar: 14 Max.nchar: 10 Max.nchar: 22 3rd Qu.:17.00
## Max. :41.00
## NAs :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 N.unique : 3 1st Qu.:0.7100
## Median : 40.00 Median :2.600 N.blank : 0 Median :0.7900
## Mean : 52.46 Mean :2.513 Min.nchar: 6 Mean :0.7922
## 3rd Qu.: 75.00 3rd Qu.:3.800 Max.nchar: 8 3rd Qu.:0.8700
## Max. :100.00 Max. :5.000 Max. :1.0000
## NAs :94 NAs :101
Nilai hilang pada pengalaman kerja, tarif per jam, dan rating diganti dengan median. Median dipilih karena tidak mudah terpengaruh oleh nilai ekstrem. Hasilnya diperiksa dengan menghitung sisa nilai hilang pada ketiga variabel tersebut, yang seharusnya bernilai nol.
# 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
## 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
## N.unique :1000 N.unique : 992 N.unique : 2 1st Qu.:31.00
## N.blank : 0 N.blank : 0 N.blank : 0 Median :41.00
## Min.nchar: 8 Min.nchar: 7 Min.nchar: 4 Mean :40.51
## Max.nchar: 8 Max.nchar: 25 Max.nchar: 6 3rd Qu.:51.00
## Max. :60.00
## NAs :30
## country language primary_skill years_of_experience
## Length :1000 Length :1000 Length :1000 Min. : 0.00
## N.unique : 21 N.unique : 16 N.unique : 10 1st Qu.: 3.00
## N.blank : 0 N.blank : 0 N.blank : 0 Median : 9.00
## Min.nchar: 5 Min.nchar: 5 Min.nchar: 2 Mean :11.34
## Max.nchar: 14 Max.nchar: 10 Max.nchar: 22 3rd Qu.:17.00
## Max. :41.00
## NAs :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 N.unique : 3 1st Qu.:0.7100
## Median : 40.00 Median :2.600 N.blank : 0 Median :0.7900
## Mean : 52.46 Mean :2.521 Min.nchar: 6 Mean :0.7922
## 3rd Qu.: 75.00 3rd Qu.:3.700 Max.nchar: 8 3rd Qu.:0.8700
## Max. :100.00 Max. :5.000 Max. :1.0000
## NAs :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
Pengalaman kerja dikelompokkan menjadi empat kategori berurutan,
yaitu pemula (0 sampai 10 tahun), menengah (11 sampai 20 tahun), jago
(21 sampai 30 tahun), dan master (di atas 30 tahun). Setiap kategori
diberi kode angka 1 sampai 4, sedangkan 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 ...
Statistik deskriptif dihitung pada pengalaman kerja dan tarif per jam setelah imputasi, sehingga jumlah datanya lengkap. Tabel ini memperlihatkan pusat dan sebaran data. Apabila nilai mean berbeda cukup jauh dari median, distribusi 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 |
Histogram memperlihatkan sebaran pengalaman kerja dan sebaran kategorinya. Bentuknya dibaca untuk mengetahui apakah data simetris atau miring, yang selanjutnya dibandingkan dengan hasil uji normalitas.
hist(
na.omit(data_clean$years_of_experience_clean),
main = "Histogram Pengalaman Kerja",
xlab = "Years of Experience",
col = "#A3B18A"
)hist(
na.omit(data_clean$kategori_encode),
main = "Histogram Kategori Pengalaman",
xlab = "Kode Kategori",
breaks = seq(0.5, 4.5, by = 1),
col = "#588157"
)Q-Q plot membandingkan kuantil data dengan kuantil distribusi normal. Apabila titik-titik mengikuti garis, data mendekati distribusi normal. Semakin menyimpang titik dari garis, semakin jauh data dari distribusi normal.
if (length(hourly_rate_test) >= 2) {
qqnorm(hourly_rate_test, main = "QQ Plot Hourly Rate",
col = "#588157", pch = 16)
qqline(hourly_rate_test, col = "#344E41", lwd = 2)
}if (length(experience_test) >= 2) {
qqnorm(experience_test, main = "QQ Plot Years of Experience",
col = "#588157", pch = 16)
qqline(experience_test, col = "#344E41", lwd = 2)
}Diagram pencar memperlihatkan pola hubungan antara pengalaman kerja dan tarif per jam. Titik yang membentuk pola naik atau turun menandakan adanya hubungan, sedangkan titik yang tersebar tanpa pola menandakan hubungan yang 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 = "#588157"
)Uji ini memeriksa apakah suatu variabel berdistribusi normal. H0 menyatakan data berdistribusi normal, dan H0 ditolak apabila p-value kurang dari 0,05. Hasilnya 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
Uji ini membandingkan varians tarif per jam antarkelompok gender. H0 menyatakan varians antarkelompok sama, dan H0 ditolak apabila p-value kurang dari 0,05, yang berarti varians dianggap berbeda.
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
Hasil uji normalitas menentukan apakah korelasi Pearson layak digunakan. Apabila data tidak berdistribusi normal, digunakan korelasi Spearman yang tidak mensyaratkan normalitas. H0 menyatakan tidak ada hubungan monotonik antara pengalaman kerja dan tarif per jam, dan H0 ditolak apabila p-value kurang dari 0,05. Nilai rho menunjukkan arah dan kekuatan hubungan.
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)
kekuatan <- 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")
))
cat("\nKoefisien Spearman (rho):", round(rho, 4), "\n")
cat("P-value:", signif(hasil_spearman$p.value, 4), "\n")
cat("Arah hubungan:", ifelse(rho > 0, "positif", "negatif"), "\n")
cat("Kekuatan hubungan:", kekuatan, "\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
Hasil imputasi dimasukkan kembali ke kolom aslinya, kemudian kolom sementara dihapus. Dengan demikian, dataset final memuat nama variabel yang sama dengan data awal.
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 × 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 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 ...
## freelancer_ID name gender age
## Length :1000 Length :1000 Length :1000 Min. :20.00
## N.unique :1000 N.unique : 992 N.unique : 2 1st Qu.:31.00
## N.blank : 0 N.blank : 0 N.blank : 0 Median :41.00
## Min.nchar: 8 Min.nchar: 7 Min.nchar: 4 Mean :40.51
## Max.nchar: 8 Max.nchar: 25 Max.nchar: 6 3rd Qu.:51.00
## Max. :60.00
## NAs :30
## country language primary_skill years_of_experience
## Length :1000 Length :1000 Length :1000 Min. : 0.00
## N.unique : 21 N.unique : 16 N.unique : 10 1st Qu.: 3.00
## N.blank : 0 N.blank : 0 N.blank : 0 Median : 9.00
## Min.nchar: 5 Min.nchar: 5 Min.nchar: 2 Mean :11.22
## Max.nchar: 14 Max.nchar: 10 Max.nchar: 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 N.unique : 3 1st Qu.:0.7100
## Median : 40.00 Median :2.600 N.blank : 0 Median :0.7900
## Mean : 51.29 Mean :2.521 Min.nchar: 6 Mean :0.7922
## 3rd Qu.: 75.00 3rd Qu.:3.700 Max.nchar: 8 3rd Qu.:0.8700
## Max. :100.00 Max. :5.000 Max. :1.0000
##
## kategori_pengalaman kategori_encode
## Length :1000 Min. :1.000
## N.unique : 4 1st Qu.:1.000
## N.blank : 0 Median :1.000
## Min.nchar: 4 Mean :1.661
## Max.nchar: 8 3rd Qu.:2.000
## Max. :4.000
##
## [1] 1000 14
## 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
## 0 0
## [1] 0
Integrasi data adalah penyusunan variabel yang relevan ke dalam satu
dataset yang konsisten agar lebih mudah dipakai dalam analisis statistik
dan visualisasi. Dari data final dipilih kolom-kolom yang dibutuhkan,
dan fungsi intersect() digunakan agar hanya kolom yang
benar-benar tersedia yang diambil.
## 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 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 ...
## [1] 1000 14
## [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"
kolom_analisis <- c(
"age",
"gender",
"country",
"years_of_experience",
"kategori_pengalaman",
"kategori_encode",
"hourly_rate (USD)",
"client_satisfaction",
"rating",
"is_active"
)
kolom_tersedia <- intersect(kolom_analisis, names(data_integrasi))
data_analisis <- data_integrasi[, kolom_tersedia, drop = FALSE]
head(data_analisis)## # A tibble: 6 × 10
## 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
## # ℹ 4 more variables: `hourly_rate (USD)` <dbl>, client_satisfaction <dbl>,
## # rating <dbl>, is_active <chr>
## tibble [1,000 × 10] (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" ...
## age gender country years_of_experience
## Min. :20.00 Length :1000 Length :1000 Min. : 0.00
## 1st Qu.:31.00 N.unique : 2 N.unique : 21 1st Qu.: 3.00
## Median :41.00 N.blank : 0 N.blank : 0 Median : 9.00
## Mean :40.51 Min.nchar: 4 Min.nchar: 5 Mean :11.22
## 3rd Qu.:51.00 Max.nchar: 6 Max.nchar: 14 3rd Qu.:17.00
## Max. :60.00 Max. :41.00
## NAs :30
## kategori_pengalaman kategori_encode hourly_rate (USD) client_satisfaction
## Length :1000 Min. :1.000 Min. : 20.00 Min. :0.6000
## N.unique : 4 1st Qu.:1.000 1st Qu.: 30.00 1st Qu.:0.7100
## N.blank : 0 Median :1.000 Median : 40.00 Median :0.7900
## Min.nchar: 4 Mean :1.661 Mean : 51.29 Mean :0.7922
## Max.nchar: 8 3rd Qu.:2.000 3rd Qu.: 75.00 3rd Qu.:0.8700
## Max. :4.000 Max. :100.00 Max. :1.0000
##
## rating is_active
## Min. :0.000 Length :1000
## 1st Qu.:1.500 N.unique : 3
## Median :2.600 N.blank : 0
## Mean :2.521 Min.nchar: 6
## 3rd Qu.:3.700 Max.nchar: 8
## Max. :5.000
##
Dataset hasil integrasi diperiksa kembali, meliputi dimensi, nilai hilang, baris ganda, serta sebaran rating, status keaktifan, dan kategori pengalaman.
## [1] 1000 10
## 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
## 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
Tahap terakhir adalah menyimpan dataset hasil pengolahan ke file CSV agar dapat digunakan kembali pada analisis berikutnya.
Proses data wrangling berhasil meningkatkan kualitas data
Global Freelancers Dataset melalui penyeragaman kategori,
perbaikan tipe data dan skala, penghapusan baris ganda, imputasi median,
serta pemberian label Unknown pada status yang tidak jelas.
Dataset akhir sudah bersih dan konsisten, lalu disimpan sebagai
data_analisis_final.csv untuk analisis lanjutan.
Hasil uji Shapiro-Wilk menunjukkan bahwa pengalaman kerja dan tarif per jam tidak berdistribusi normal, sehingga hubungan keduanya dianalisis dengan korelasi Spearman. Hasilnya menunjukkan hubungan positif yang signifikan secara statistik, tetapi kekuatannya tergolong sangat lemah. Uji Bartlett menunjukkan varians tarif per jam antara kelompok gender tidak berbeda nyata.
Dengan demikian, pengalaman kerja berkaitan dengan tarif per jam, tetapi pengaruhnya kecil. Faktor lain di luar variabel yang dianalisis kemungkinan berperan lebih besar dalam menentukan besarnya tarif freelancer.