1. Pendahuluan

1.1 Latar Belakang

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.

1.2 Rumusan Masalah

  1. Bagaimana kondisi dan kualitas data pada dataset freelancer?
  2. Permasalahan data apa saja yang terdapat pada dataset tersebut, seperti missing value, inkonsistensi, dan kesalahan format?
  3. Bagaimana proses pembersihan dan transformasi data agar data menjadi lebih rapi dan siap digunakan?
  4. Bagaimana karakteristik data setelah proses pembersihan?
  5. Bagaimana hubungan antara pengalaman kerja (years of experience) dan tarif per jam (hourly rate) freelancer?

1.3 Tujuan

  1. Mengidentifikasi kondisi dan kualitas data pada dataset freelancer.
  2. Menemukan dan mengevaluasi permasalahan data, seperti missing value, inkonsistensi, dan kesalahan format.
  3. Melakukan pembersihan dan transformasi data, agar data menjadi lebih rapi dan siap digunakan.
  4. Menganalisis karakteristik data setelah pembersihan, sebagai dasar untuk analisis selanjutnya.
  5. Menganalisis hubungan antara pengalaman kerja (years of experience) dan tarif per jam (hourly rate) freelancer.

2. Gambaran Dataset

2.1 Sumber dan Variabel Data

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

2.2 Import Dataset

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.

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)

2.3 Struktur Awal Data

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.

cat("Jumlah baris:", nrow(ADE), "\n")
## Jumlah baris: 1000
cat("Jumlah kolom:", ncol(ADE), "\n\n")
## Jumlah kolom: 12
names(ADE)
##  [1] "freelancer_ID"       "name"                "gender"             
##  [4] "age"                 "country"             "language"           
##  [7] "primary_skill"       "years_of_experience" "hourly_rate (USD)"  
## [10] "rating"              "is_active"           "client_satisfaction"
str(ADE)
## 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 ...
head(ADE)
## # 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>
tail(ADE)
## # 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>
summary(ADE)
##    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

3. Permasalahan Data

3.1 Missing Value

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.

colSums(is.na(ADE))
##       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
round(colMeans(is.na(ADE)) * 100, 2)
##       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
cat("Total missing value:", sum(is.na(ADE)), "\n")
## Total missing value: 541

3.2 Inkonsistensi Gender

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.

table(ADE$gender, useNA = "ifany")
## 
##      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

3.3 Wrangling Hourly Rate

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.

unique(ADE$`hourly_rate (USD)`)
##  [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 ...

3.4 Wrangling Client Satisfaction

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.

unique(ADE$client_satisfaction)
##  [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
# Isi NA dengan median
sum(is.na(ADE$client_satisfaction))
## [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
sum(is.na(ADE$client_satisfaction))
## [1] 0

3.5 Wrangling Age, Years of Experience, dan Rating

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.

unique(ADE$age)
##  [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 ...

3.6 Wrangling Is_Active

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.

table(ADE$is_active, useNA = "ifany")
## 
##     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
sum(is.na(ADE$is_active))
## [1] 0

3.7 Duplikasi Data

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.

sum(duplicated(ADE))
## [1] 0
ADE[duplicated(ADE), ]
## # 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>
ADE <- ADE[!duplicated(ADE), ]

sum(duplicated(ADE))
## [1] 0
dim(ADE)
## [1] 1000   12

3.8 Data Noisy

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.

summary(ADE$age)
##    Min. 1st Qu.  Median    Mean 3rd Qu.    Max.     NAs 
##   20.00   31.00   41.00   40.51   51.00   60.00      30
sum(ADE$age < 0, na.rm = TRUE)
## [1] 0
sum(ADE$age > 100, na.rm = TRUE)
## [1] 0
summary(ADE$years_of_experience)
##    Min. 1st Qu.  Median    Mean 3rd Qu.    Max.     NAs 
##    0.00    3.00    9.00   11.34   17.00   41.00      51
sum(ADE$years_of_experience < 0, na.rm = TRUE)
## [1] 0
summary(ADE$rating)
##    Min. 1st Qu.  Median    Mean 3rd Qu.    Max.     NAs 
##   0.000   1.400   2.600   2.513   3.800   5.000     101
sum(ADE$rating < 0, na.rm = TRUE)
## [1] 0
sum(ADE$rating > 5, na.rm = TRUE)
## [1] 0
sum(ADE$rating == 0, na.rm = TRUE)
## [1] 145
summary(ADE$`hourly_rate (USD)`)
##    Min. 1st Qu.  Median    Mean 3rd Qu.    Max.     NAs 
##   20.00   30.00   40.00   52.46   75.00  100.00      94
sum(ADE$`hourly_rate (USD)` < 0, na.rm = TRUE)
## [1] 0
summary(ADE$client_satisfaction)
##    Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
##  0.6000  0.7100  0.7900  0.7922  0.8700  1.0000
sum(ADE$client_satisfaction < 0, na.rm = TRUE)
## [1] 0
sum(ADE$client_satisfaction > 1, na.rm = TRUE)
## [1] 0

4. Data Hasil Wrangling

4.1 Membuat Data Hasil Wrangling

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.

data_clean <- ADE

str(data_clean)
## 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 ...
dim(data_clean)
## [1] 1000   12
summary(data_clean)
##    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

4.2 Mengisi Missing Value dengan Median

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
sum(is.na(data_clean$hourly_rate_clean))
## [1] 0
sum(is.na(data_clean$rating))
## [1] 0

5. Validasi Setelah Wrangling

str(data_clean)
## 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 ...
dim(data_clean)
## [1] 1000   14
summary(data_clean)
##    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   
## 
colSums(is.na(data_clean))
##             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
round(colMeans(is.na(data_clean)) * 100, 2)
##             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
cat("Baris duplikat:", sum(duplicated(data_clean)), "\n")
## Baris duplikat: 0
cat("Usia di luar 0-100:",
    sum(data_clean$age < 0 | data_clean$age > 100, na.rm = TRUE), "\n")
## Usia di luar 0-100: 0
cat("Pengalaman kerja negatif:",
    sum(data_clean$years_of_experience_clean < 0, na.rm = TRUE), "\n")
## Pengalaman kerja negatif: 0
cat("Rating di luar 0-5:",
    sum(data_clean$rating < 0 | data_clean$rating > 5, na.rm = TRUE), "\n")
## Rating di luar 0-5: 0
cat("Hourly rate negatif:",
    sum(data_clean$hourly_rate_clean < 0, na.rm = TRUE), "\n")
## 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

6. Transformasi dan Visualisasi

6.1 Encoding Pengalaman Kerja

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.

library(dplyr)

max(data_clean$years_of_experience_clean, na.rm = TRUE)
## [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
str(data_clean$kategori_encode)
##  num [1:1000] 2 4 4 1 3 2 1 2 1 3 ...

6.2 Statistik Deskriptif

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

6.3 Histogram

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"
)

6.4 Q-Q Plot

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)
}

6.5 Scatter Plot

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"
)

7. Uji Asumsi

7.1 Uji Normalitas (Shapiro-Wilk)

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

7.2 Uji Homogenitas Varians (Bartlett)

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

7.3 Korelasi Spearman

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

8. Dataset Final

8.1 Membuat Data Final

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 ...
summary(data_final)
##    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  
## 
dim(data_final)
## [1] 1000   14
colSums(is.na(data_final))
##       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
sum(duplicated(data_final))
## [1] 0

8.2 Integrasi Data

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.

data_integrasi <- data_final

str(data_integrasi)
## 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 ...
dim(data_integrasi)
## [1] 1000   14
names(data_integrasi)
##  [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>
str(data_analisis)
## 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" ...
summary(data_analisis)
##       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                   
## 

8.3 Validasi Hasil Integrasi

Dataset hasil integrasi diperiksa kembali, meliputi dimensi, nilai hilang, baris ganda, serta sebaran rating, status keaktifan, dan kategori pengalaman.

dim(data_analisis)
## [1] 1000   10
colSums(is.na(data_analisis))
##                 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
sum(duplicated(data_analisis))
## [1] 0
summary(data_analisis$rating)
##    Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
##   0.000   1.500   2.600   2.521   3.700   5.000
sum(is.na(data_analisis$rating))
## [1] 0
table(data_analisis$is_active, useNA = "ifany")
## 
##   Active Inactive  Unknown 
##      446      465       89
table(data_analisis$kategori_pengalaman, useNA = "ifany")
## 
##     jago   master menengah   pemula 
##      122       55      252      571

9. Menyimpan Hasil

Tahap terakhir adalah menyimpan dataset hasil pengolahan ke file CSV agar dapat digunakan kembali pada analisis berikutnya.

write.csv(
  data_analisis,
  "data_analisis_final.csv",
  row.names = FALSE
)

message("Proses selesai. Dataset final telah disimpan.")

10. Kesimpulan

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.