I. Pendahuluan

I.1 Latar Belakang

Perkembangan teknologi digital membuka peluang bagi masyarakat untuk bekerja secara lepas tanpa harus tetap berada di satu tempat kerja tertentu. Di pasar kerja ini, freelancer memiliki ciri-ciri yang berbeda-beda, seperti usia, keterampilan, pengalaman kerja, serta harga jasa yang mereka berikan. Pengalaman kerja yang lama (years of experience) menjadi salah satu hal yang menarik untuk dikaji karena diduga berhubungan dengan besarnya tarif per jam (hourly rate).

Analisis ini memakai Global Freelancers Dataset yang didapatkan dari Kaggle. Dataset tersebut terdiri dari 1.000 data freelancer dan 12 variabel yang mencakup informasi mengenai karakteristik pribadi, pengalaman kerja, biaya jasa, penilaian, tingkat kepuasan klien, serta kondisi aktIIIitas mereka.

Sebelum dianalisis, data harus diperiksa terlebih dahulu untuk mengetahui adakah nilai yang hilang, ketidakkonsistenan pada kategori, kesalahan dalam format, adanya duplikasi, serta nilai-nilai yang mungkin tidak masuk akal. Proses pembersihan dan pengolahan data dilakukan agar informasi yang digunakan menjadi lebih konsisten. Selanjutnya, analisis dilakukan untuk menggambarkan ciri-ciri pengalaman kerja dan mempelajari hubungannya dengan upah per jam yang diterima oleh freelancer.

I.2 Rumusan Masalah

  1. Apa saja permasalahan kualitas data yang terdapat pada Global Freelancers Dataset?
  2. Bagaimana proses preprocessing dilakukan untuk menangani nilai yang hilang, inkonsistensi format, dan ketidaksesuaian tipe data?
  3. Bagaimana karakteristik pengalaman kerja freelancer setelah pembersihan data berdasarkan statistik deskriptif dan pengujian normalitas?
  4. Bagaimana hubungan antara pengalaman kerja dan tarif per jam freelancer?

I.3 Tujuan

  1. Mengidentifikasi permasalahan kualitas data yang terdapat pada Global Freelancers Dataset.
  2. Melakukan preprocessing untuk menangani nilai yang hilang, menyeragamkan format, dan memperbaiki tipe data.
  3. Mendeskripsikan karakteristik pengalaman kerja melalui statistik deskriptif dan pemeriksaan normalitas setelah pembersihan data.
  4. Menganalisis hubungan antara pengalaman kerja dan tarif per jam freelancer.

II. Gambaran Dataset

II.1 Informasi Dataset

Dataset ini menyimpan data tentang profil para freelancer dari berbagai negara, seperti informasi demografi, pengalaman bekerja, harga jasa per jam, nilai rating, tingkat kepuasan klien, serta status apakah mereka masih aktif atau tidak.

II.2 Sumber Data

Data diperoleh dari Kaggle dengan nama file data ade global_freelancers_raw.xlsx.

II.3 Jenis Data

Data yang digunakan mencakup variabel kuantitatif dan kualitatif. Data berbentuk potret observasi pada sejumlah freelancer, sehingga analisis dilakukan berdasarkan informasi yang tersedia dalam dataset.

II.4. Import Dataset

library(readxl)
ADE<- read_excel("C:\\Users\\lenovo\\Documents\\cool yeah\\data ade global_freelancers_raw.xlsx")

cek dataset

print(ADE)
## # A tibble: 1,000 × 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 D…
##  2 FL250002      Vanessa Garcia  FEMALE 52.0  Australia   English  Mobile Apps  
##  3 FL250003      Juan Nelson     male   53.0  Germany     German   Graphic Desi…
##  4 FL250004      Amanda Spencer  F      38.0  Australia   English  Web Developm…
##  5 FL250005      Lynn Curtis DDS female 53.0  Germany     German   Web Developm…
##  6 FL250006      Lisa Johnson    female 59.0  Netherlands Dutch    AI           
##  7 FL250007      Eric Myers      m      52.0  Indonesia   Indones… Data Analysis
##  8 FL250008      Ricky Graham    male   43.0  Italy       Italian  Blockchain D…
##  9 FL250009      Sean Martin     male   26.0  United Sta… English  Blockchain D…
## 10 FL250010      Matthew Lloyd   MALE   52.0  Turkey      Turkish  AI           
## # ℹ 990 more rows
## # ℹ 5 more variables: years_of_experience <chr>, `hourly_rate (USD)` <chr>,
## #   rating <chr>, is_active <chr>, client_satisfaction <dbl>

III. Identifikasi Masaalah

yaapping dikit dikit

III.1 Duplikat

lorem ipsum

sum(duplicated(ADE))
## [1] 0

Sintaks ini dipakai untuk melihat berapa jumlah data duplikat pada dataframe.

III.2 Data Tidak Konsisten

Kita bisa panggil 10 baris pertama dataset kita dengan

head(ADE,10)
## # A tibble: 10 × 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 D…
##  2 FL250002      Vanessa Garcia  FEMALE 52.0  Australia   English  Mobile Apps  
##  3 FL250003      Juan Nelson     male   53.0  Germany     German   Graphic Desi…
##  4 FL250004      Amanda Spencer  F      38.0  Australia   English  Web Developm…
##  5 FL250005      Lynn Curtis DDS female 53.0  Germany     German   Web Developm…
##  6 FL250006      Lisa Johnson    female 59.0  Netherlands Dutch    AI           
##  7 FL250007      Eric Myers      m      52.0  Indonesia   Indones… Data Analysis
##  8 FL250008      Ricky Graham    male   43.0  Italy       Italian  Blockchain D…
##  9 FL250009      Sean Martin     male   26.0  United Sta… English  Blockchain D…
## 10 FL250010      Matthew Lloyd   MALE   52.0  Turkey      Turkish  AI           
## # ℹ 5 more variables: years_of_experience <chr>, `hourly_rate (USD)` <chr>,
## #   rating <chr>, is_active <chr>, client_satisfaction <dbl>

Bisa dilihat ada penulisan yag tidak konsisten pada dataset ini.

unique(ADE$gender)
##  [1] "f"      "FEMALE" "male"   "F"      "female" "m"      "MALE"   "Female"
##  [9] "M"      "Male"

Contohnya pada kolom “gender” dimana terdapat 9 penamaan gender yang berbeda.

III.3 Missing Values

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

Sintaks ini dipakai untuk melihat apakah ada data kosong pada tiap kolom tabel, bisa dilihat terdapat banyak data kosong pada data set ini.

III.4 Inkonsistensi data

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 ...

Sintaks ini dipakai untuk melihat strutur data, bisa dilihat bahwa kolom “age”,“years_of_experience”,“hourly_rate (USD)”, dan “rating” tersimpan sebagai chr atau karakter dan bukan numerik.

III.5 Data Tidak Masuk akal

age

sum(ADE$age < 0, na.rm = TRUE)
## [1] 0
sum(ADE$age > 100, na.rm = TRUE)
## [1] 970

years_of_experience

sum(ADE$years_of_experience < 0, na.rm = TRUE)
## [1] 0

rating

sum(ADE$rating < 0, na.rm = TRUE)
## [1] 0
sum(ADE$rating > 5, na.rm = TRUE)
## [1] 10

hourly_rate

sum(ADE$`hourly_rate (USD)` < 0, na.rm = TRUE)
## [1] 227

client_satisfaction

sum(ADE$client_satisfaction < 0, na.rm = TRUE)
## [1] 0
sum(ADE$client_satisfaction > 100, na.rm = TRUE)
## [1] 0

III.6 Data Noisy

Pemeriksaan dilakukan terhadap ringkasan nilai untuk menemukan nilai yang berpotensi tidak wajar. Nilai ekstrem tidak otomatis dihapus sebelum diperiksa lebih lanjut.

variabel_numerik <- c(
  "age",
  "years_of_experience",
  "rating",
  "hourly_rate (USD)",
  "client_satisfaction"
)

for (v in intersect(variabel_numerik, names(ADE))) {
  cat("\nVariabel:", v, "\n")
  print(summary(ADE[[v]]))
}
## 
## Variabel: age 
##    Length     Class      Mode 
##      1000 character character 
## 
## Variabel: years_of_experience 
##    Length     Class      Mode 
##      1000 character character 
## 
## Variabel: rating 
##    Length     Class      Mode 
##      1000 character character 
## 
## Variabel: hourly_rate (USD) 
##    Length     Class      Mode 
##      1000 character character 
## 
## Variabel: client_satisfaction 
##    Min. 1st Qu.  Median    Mean 3rd Qu.    Max.    NA's 
##    0.60    0.71    0.82   11.73    0.94  100.00     176

IV. Wrangling

IV.1 Standarisasi Data

Kolom Gender

Pertma kita elihat apa saja penamaan gender yan ada pada kolom ini.

unique(ADE$gender)
##  [1] "f"      "FEMALE" "male"   "F"      "female" "m"      "MALE"   "Female"
##  [9] "M"      "Male"

Kemudian kita lakukan cleaning.

ADE$gender <- tolower(trimws(ADE$gender))
ADE$gender[ADE$gender %in% c("f", "female")] <- "Female"
ADE$gender[ADE$gender %in% c("m", "male")] <- "Male"

Pertama kita membuat semua hurufnya menjadi lowercase atau huruf kecil. Setelah itu kita melakukan standarisasi pada data. Kemudian lihat hasilnya

table(
  ADE$gender,
  useNA = "ifany"
)
## 
## Female   Male 
##    490    510

Kolom Hourly Rate

Pertama kita melihat apa-apa saja data pada kolom ini.

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"

Setelah itu, kita akan menghapus simbol “$” dann “USD” dengan sintaks berikut.

ADE$`hourly_rate (USD)` <- gsub("\\$", "", ADE$`hourly_rate (USD)`)
ADE$`hourly_rate (USD)` <- gsub("USD", "", ADE$`hourly_rate (USD)`)

Kemudian kita akan menghapus spasi yang ada pada data tersebut dengan sintaks.

ADE$`hourly_rate (USD)` <- trimws(ADE$`hourly_rate (USD)`)

Terakhir kita ubah formatnya menjadi numerik.

ADE$`hourly_rate (USD)` <- as.numeric(ADE$`hourly_rate (USD)`)

Kolom Client Satisfaction

untuk ini kita akan mekakai sebuah fungsi.

ubah_numerik <- function(x) {
  
  x <- trimws(
    as.character(x)
  )
  
  x[x == ""] <- NA_character_
  
  x <- gsub(
    ",",
    "",
    x
  )
  
  x <- gsub(
    "USD",
    "",
    x,
    ignore.case = TRUE
  )
  
  x <- gsub(
    "\\$",
    "",
    x
  )
  
  x <- gsub(
    "%",
    "",
    x
  )
  
  suppressWarnings(
    as.numeric(
      trimws(x)
    )
  )
}

kemudian

ubah_numerik <- function(x) {
  
  x <- trimws(
    as.character(x)
  )
  
  x[x == ""] <- NA_character_
  
  x <- gsub(
    ",",
    "",
    x
  )
  
  x <- gsub(
    "USD",
    "",
    x,
    ignore.case = TRUE
  )
  
  x <- gsub(
    "\\$",
    "",
    x
  )
  
  x <- gsub(
    "%",
    "",
    x
  )
  
  suppressWarnings(
    as.numeric(
      trimws(x)
    )
  )
}

Kolomm Numerik Lainnya

variabel_num <- c(
  "age",
  "years_of_experience",
  "rating"
)

for (v in intersect(
  variabel_num,
  names(ADE)
)) {
  
  ADE[[v]] <- ubah_numerik(
    ADE[[v]]
  )
}

Kolom is active

Pertama kita hapus spasi dan membbuat data ditulis dalam huruf kecil.

active_raw <- tolower(
  trimws(as.character(ADE$is_active))
)

Kemudian kita cek apa saja data didalamnya.

unique(ADE$is_active)
## [1] "0"     "1"     "N"     "FALSE" "TRUE"  "yes"   "Y"     NA      "no"

Setelah itu kita standarisasi.

  ADE$is_active <- ifelse(
    is.na(active_raw) | active_raw == "",
    "Unknown",
    ifelse(
      active_raw %in% c("1", "y", "yes", "true", "active"),
      "Active",
      ifelse(
        active_raw %in% c("0", "n", "no", "false", "inactive"),
        "Inactive",
        "Unknown")))

Periksa Hasil Standarisasi

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] "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] NA 0.84 0.71 0.9 0.83 NA 0.94 0.94 0.76 NA ...
if ("gender" %in% names(ADE)) {
  print(
    table(
      ADE$gender,
      useNA = "ifany"
    )
  )
}
## 
## Female   Male 
##    490    510
if ("is_active" %in% names(ADE)) {
  print(
    table(
      ADE$is_active,
      useNA = "ifany"
    )
  )
}
## 
##   Active Inactive  Unknown 
##      446      465       89

IV.2 Menghapus Data Noisy

Pada tahap ini, kita memeriksa nilai terlebih dahulu. Nilai yang ekstrem tidak langsung dihapus karena perlu dibedakan antara nilai yang memang ekstrem dan nilai yang merupakan kesalahan pencatatan.

for (v in intersect(
  variabel_numerik,
  names(ADE)
)) {
  
  cat(
    "\nRingkasan:",
    v,
    "\n"
  )
  
  print(
    summary(ADE[[v]])
  )
}
## 
## Ringkasan: age 
##    Min. 1st Qu.  Median    Mean 3rd Qu.    Max.    NA's 
##   20.00   31.00   41.00   40.51   51.00   60.00      30 
## 
## Ringkasan: years_of_experience 
##    Min. 1st Qu.  Median    Mean 3rd Qu.    Max.    NA's 
##    0.00    3.00    9.00   11.34   17.00   41.00      51 
## 
## Ringkasan: rating 
##    Min. 1st Qu.  Median    Mean 3rd Qu.    Max.    NA's 
##   0.000   1.400   2.600   2.513   3.800   5.000     101 
## 
## Ringkasan: hourly_rate (USD) 
##    Min. 1st Qu.  Median    Mean 3rd Qu.    Max.    NA's 
##   20.00   30.00   40.00   52.46   75.00  100.00      94 
## 
## Ringkasan: client_satisfaction 
##    Min. 1st Qu.  Median    Mean 3rd Qu.    Max.    NA's 
##    0.60    0.71    0.82   11.73    0.94  100.00     176
if ("age" %in% names(ADE)) {
  
  cat(
    "Usia negatif:",
    sum(
      ADE$age < 0,
      na.rm = TRUE
    ),
    "\n"
  )
}
## Usia negatif: 0
if ("years_of_experience" %in% names(ADE)) {
  
  cat(
    "Pengalaman kerja negatif:",
    sum(
      ADE$years_of_experience < 0,
      na.rm = TRUE
    ),
    "\n"
  )
}
## Pengalaman kerja negatif: 0
if ("hourly_rate (USD)" %in% names(ADE)) {
  
  cat(
    "Hourly rate negatif:",
    sum(
      ADE$`hourly_rate (USD)` < 0,
      na.rm = TRUE
    ),
    "\n"
  )
}
## Hourly rate negatif: 0

IV.3 Mengisi Missing Value

Imputasi median dilakukan pada variabel numerik yang diperlukan dalam analisis. Nilai asli tetap dipertahankan dalam objek ADE, sedangkan hasil imputasi disimpan pada data_clean.

data_clean <- ADE

rating

Kami mengisi data kosong pada kolom rating dengan nilai tengah atau median.

median_rating <- median(
  data_clean$rating,
  na.rm = TRUE
)

pertama kita cari nilai mediannya dengan kode diatas, kemudian isi nilai kosongnya dengan kode berikut.

data_clean$rating[
  is.na(data_clean$rating)
] <- median_rating

cek kembali

sum(is.na(data_clean$rating))
## [1] 0

client satisfaction

Pertama cari nilai mediannya.

median_sat <- median(
  data_clean$client_satisfaction,
  na.rm = TRUE
)

kemudian masukkan nilainya ke data yang kosong

data_clean$client_satisfaction[
  is.na(data_clean$client_satisfaction)
] <- median_sat

slkjda

sum(is.na(data_clean$client_satisfaction))
## [1] 0

years_of_experience

disini kami membuat kolom baru yang berisi dat ayang sudah dibersigkan.

# 1. Hitung nilai median (gunakan na.rm = TRUE agar NA diabaikan saat perhitungan)
median_exp <- median(data_clean$years_of_experience, na.rm = TRUE)

# 2. Isi nilai NA menggunakan ifelse
data_clean$years_of_experience_clean <- ifelse(
  is.na(data_clean$years_of_experience),
  median_exp,
  data_clean$years_of_experience
)

cek

sum(is.na(data_clean$years_of_experience_clean))
## [1] 0

hourly rate

Pertama karna kita mau mengisi data kosong dengan nilai median, maka kita harus cari dulu nilai mediannya.

median_rate <- median(
  data_clean$`hourly_rate (USD)`,
  na.rm = TRUE
)

Setelah itu kita bisa langsung memasukkan nilai median tersebut ke kolom baru.

data_clean$hourly_rate_clean  <- ifelse(
  is.na(data_clean$`hourly_rate (USD)`),
  median_exp,
  data_clean$`hourly_rate (USD)`
)

cek.

sum(is.na(data_clean$hourly_rate_clean))
## [1] 0

Periksa kembali

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

IV.4 Hasil Akhir Pembersihan

data_final <- data_clean

# Memindahkan hasil imputasi ke variabel analisis
if ("years_of_experience_clean" %in% names(data_final)) {
  data_final$years_of_experience <-
    data_final$years_of_experience_clean

  
}

if ("hourly_rate_clean" %in% names(data_final)) {
  data_final$`hourly_rate (USD)` <-
    data_final$hourly_rate_clean

  
}

cat("Dimensi data final:\n")
## Dimensi data final:
print(dim(data_final))
## [1] 1000   14
cat("\nStruktur data final:\n")
## 
## Struktur data final:
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 9 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.82 0.84 0.71 0.9 0.83 0.82 0.94 0.94 0.76 0.82 ...
##  $ 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 9 50 ...
cat("\nMissing value setelah pembersihan:\n")
## 
## Missing value setelah pembersihan:
print(colSums(is.na(data_final)))
##             freelancer_ID                      name                    gender 
##                         0                         0                         0 
##                       age                   country                  language 
##                        30                         0                         0 
##             primary_skill       years_of_experience         hourly_rate (USD) 
##                         0                         0                         0 
##                    rating                 is_active       client_satisfaction 
##                         0                         0                         0 
## years_of_experience_clean         hourly_rate_clean 
##                         0                         0

V. Tujuan Analisis

Analisis dilakukan untuk mengetahui bagaimana pengalaman kerja freelancer tersebar dan menilai hubungan antara pengalaman tersebut dengan tarif jasa per jam yang mereka kenakan. Statistik deskriptif, visualisasi, dan analisis korelasi digunakan untuk membantu menjelaskan pola hubungan antara dua variabel.

V.1 Statistik Deskriptif

if ("years_of_experience" %in% names(data_final)) {
  print(summary(data_final$years_of_experience))
  print(sd(data_final$years_of_experience, na.rm = TRUE))
}
##    Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
##    0.00    3.00    9.00   11.22   17.00   41.00 
## [1] 9.444329
if ("hourly_rate (USD)" %in% names(data_final)) {
  print(summary(data_final$`hourly_rate (USD)`))
  print(sd(data_final$`hourly_rate (USD)`, na.rm = TRUE))
}
##    Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
##    9.00   30.00   40.00   48.38   75.00  100.00 
## [1] 28.93724

VI. Validasi Data

Missing value dan duplikasi

cat("Missing value per variabel:\n")
## Missing value per variabel:
print(colSums(is.na(data_final)))
##             freelancer_ID                      name                    gender 
##                         0                         0                         0 
##                       age                   country                  language 
##                        30                         0                         0 
##             primary_skill       years_of_experience         hourly_rate (USD) 
##                         0                         0                         0 
##                    rating                 is_active       client_satisfaction 
##                         0                         0                         0 
## years_of_experience_clean         hourly_rate_clean 
##                         0                         0
cat("\nJumlah baris duplikat:\n")
## 
## Jumlah baris duplikat:
print(sum(duplicated(data_final)))
## [1] 0

Validasi usia

if ("age" %in% names(data_final)) {
  cat(
    "\nUsia di luar rentang 0-100:",
    sum(
      data_final$age < 0 | data_final$age > 100,
      na.rm = TRUE
    ),
    "\n"
  )
}
## 
## Usia di luar rentang 0-100: 0

Validasi years_of_experience

if ("years_of_experience" %in% names(data_final)) {
  cat(
    "Pengalaman kerja negatif:",
    sum(data_final$years_of_experience < 0, na.rm = TRUE),
    "\n"
  )
}
## Pengalaman kerja negatif: 0

Validasi hourly rate

if ("hourly_rate (USD)" %in% names(data_final)) {
  cat(
    "Hourly rate negatif:",
    sum(data_final$`hourly_rate (USD)` < 0, na.rm = TRUE),
    "\n"
  )
}
## Hourly rate negatif: 0

VII. Uji Asumsi

VII.1 Uji Normalitas (Shapiro-Wilk)

Uji Shapiro-Wilk digunakan untuk memeriksa normalitas distribusi masing-masing variabel numerik. Hasilnya perlu ditafsirkan bersama histogram dan Q-Q plot.

data_final$years_of_experience <- as.numeric(
  as.character(data_final$years_of_experience)
)

data_final$`hourly_rate (USD)` <- as.numeric(
  as.character(data_final$`hourly_rate (USD)`)
)

experience_test <- na.omit(
  data_final$years_of_experience
)

hourly_rate_test <- na.omit(
  data_final$`hourly_rate (USD)`
)

if (length(experience_test) >= 3 &&
    length(experience_test) <= 5000) {
  cat("Uji normalitas pengalaman kerja:\n")
  print(shapiro.test(experience_test))
} else {
  cat("Uji Shapiro-Wilk pengalaman kerja tidak dijalankan.\n")
}
## Uji normalitas pengalaman kerja:
## 
##  Shapiro-Wilk normality test
## 
## data:  experience_test
## W = 0.9101, p-value < 2.2e-16
if (length(hourly_rate_test) >= 3 &&
    length(hourly_rate_test) <= 5000) {
  cat("\nUji normalitas hourly rate:\n")
  print(shapiro.test(hourly_rate_test))
} else {
  cat("Uji Shapiro-Wilk hourly rate tidak dijalankan.\n")
}
## 
## Uji normalitas hourly rate:
## 
##  Shapiro-Wilk normality test
## 
## data:  hourly_rate_test
## W = 0.88938, p-value < 2.2e-16

VII.2 Uji Homogenitas Varians (Bartlett)

Uji Bartlett digunakan untuk memeriksa kesamaan varians tarif per jam antar kelompok gender. Pengujian ini mensyaratkan data numerik dan kelompok yang memadai. Uji ini sensitif terhadap ketidaknormalan data.

if ("gender" %in% names(data_final) &&
    "hourly_rate (USD)" %in% names(data_final)) {

  data_bartlett <- data_final[
    !is.na(data_final$`hourly_rate (USD)`) &
      !is.na(data_final$gender) &
      data_final$gender != "Unknown",
  ]

  data_bartlett$gender <- factor(data_bartlett$gender)

  if (nlevels(droplevels(data_bartlett$gender)) >= 2) {
    print(
      bartlett.test(
        `hourly_rate (USD)` ~ gender,
        data = data_bartlett
      )
    )
  } else {
    cat("Uji Bartlett tidak dilakukan karena kelompok kurang dari dua.\n")
  }
}
## 
##  Bartlett test of homogeneity of variances
## 
## data:  hourly_rate (USD) by gender
## Bartlett's K-squared = 0.87851, df = 1, p-value = 0.3486

VII.3 Analisis Korelasi

Korelasi Spearman digunakan untuk menilai arah dan kekuatan hubungan monotonik antara pengalaman kerja dan tarif per jam. Analisis dilakukan hanya pada observasi yang memiliki kedua nilai tersebut.

data_korelasi <- data.frame(
  pengalaman = data_final$years_of_experience,
  tarif = data_final$`hourly_rate (USD)`
)

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

  print(
    cor.test(
      data_korelasi$pengalaman,
      data_korelasi$tarif,
      method = "spearman",
      exact = FALSE
    )
  )
} else {
  cat("Data tidak cukup bervariasi untuk menghitung korelasi.\n")
}
## 
##  Spearman's rank correlation rho
## 
## data:  data_korelasi$pengalaman and data_korelasi$tarif
## S = 153632972, p-value = 0.01337
## alternative hypothesis: true rho is not equal to 0
## sample estimates:
##        rho 
## 0.07820124

VIII Integrasi Data

VIII.1 Pengertian

Integrasi data adalah proses menyatukan informasi dari sumber atau tabel yang berbeda agar bisa digunakan bersama dalam analisis. Dalam penelitian ini, proses pengolahan juga melibatkan menggabungkan variabel hasil pembersihan dan variabel turunan ke dalam dataset yang digunakan untuk analisis.

VIII.2 Tujuan

Integrasi bertujuan mengumpulkan variabel-variabel yang penting ke dalam satu dataset yang sama dan konsisten, sehingga memudahkan penggunaan dalam analisis statistik dan pembuatan visualisasi.

VIII.3 Encoding Pengalaman Kerja

Kita akan memberikn nama pada kategori pengalaman per 10 tahun.

data_final$kategori_pengalaman <-
  ifelse(data_final$years_of_experience <= 10, "pemula",
         ifelse(data_final$years_of_experience <= 20, "menengah",
                ifelse(data_final$years_of_experience <= 30, "jago",
                       "master")))

Kemudian kita encoding kategori tersebut dengan ordinal encoding.

data_final$kategori_encode <- as.numeric(
  factor(
    data_final$kategori_pengalaman,
    levels = c("pemula", "menengah", "jago", "master"),
    labels = c(1, 2, 3, 4)
  )
)
table(
  data_final$kategori_pengalaman,
  useNA = "ifany"
)
## 
##     jago   master menengah   pemula 
##      122       55      252      571
table(
  data_final$kategori_encode,
  useNA = "ifany"
)
## 
##   1   2   3   4 
## 571 252 122  55

VIII.4 Visuallisasi Data

Histogram pengalaman kerja

hist(
  data_final$years_of_experience,
  main =
    "Distribusi Pengalaman Kerja Freelancer",
  xlab =
    "Years of Experience"
)

Q_Q Plot pengalaman

qqnorm(
  data_final$years_of_experience,
  main =
    "Q-Q Plot Pengalaman Kerja"
)

qqline(
  na.omit(
    data_final$years_of_experience
  )
)

Scatter Plot Pengalaman Kerja dan Hourly Rate

plot(
  data_final$years_of_experience,
  data_final$`hourly_rate (USD)`,
  main =
    "Pengalaman Kerja dan Hourly Rate",
  xlab =
    "Years of Experience",
  ylab =
    "Hourly Rate (USD)",
  pch = 19
)

data_plot <- data.frame(
  pengalaman =
    data_final$years_of_experience,
  tarif =
    data_final$`hourly_rate (USD)`
)

data_plot <- data_plot[
  complete.cases(data_plot),
]

if (
  nrow(data_plot) >= 2 &&
  length(
    unique(data_plot$pengalaman)
  ) >= 2
) {
  
  abline(
    lm(
      tarif ~ pengalaman,
      data = data_plot
    ),
    lwd = 2
  )
}

VIII.5 Data Gabungan

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

data_analisis <- data_final[
  ,
  kolom_tersedia,
  drop = FALSE
]

cat(
  "Dimensi data analisis:\n"
)
## Dimensi data analisis:
print(
  dim(data_analisis)
)
## [1] 1000   10
cat(
  "\nKolom yang digunakan:\n"
)
## 
## Kolom yang digunakan:
print(
  names(data_analisis)
)
##  [1] "age"                 "gender"              "country"            
##  [4] "years_of_experience" "kategori_pengalaman" "kategori_encode"    
##  [7] "hourly_rate (USD)"   "client_satisfaction" "rating"             
## [10] "is_active"
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>

#IX. Transformasi Numerik

Transformasi numerik dilakukan untuk mengubah skala atau bentuk distribusi variabel numerik agar lebih sesuai untuk digunakan dalam analisis eksploratif dan analisis statistik lanjutan. Pada tahap ini, variabel years_of_experience_clean dan hourly_rate_clean ditransformasikan menggunakan beberapa metode, yaitu normalisasi Min-Max, standardisasi Z-Score, dan transformasi Inverse Square.

##IX.1 Normalisasi Min-Max

Normalisasi Min-Max digunakan untuk mengubah nilai suatu variabel ke dalam rentang tertentu, yaitu antara 0 dan 1. Transformasi ini dilakukan pada variabel years_of_experience_clean dan hourly_rate_clean. Sebelum transformasi dilakukan, rentang nilai minimum dan maksimum dari kedua variabel diperiksa terlebih dahulu.

# Load library pendukung 
library(dplyr)
library(ggplot2)

# 1. Pemeriksaan Rentang Data Awal
data_final %>%
  summarise(
    across(
      c(years_of_experience_clean, hourly_rate_clean),
      list(
        min = ~ min(.x, na.rm = TRUE),
        max = ~ max(.x, na.rm = TRUE)
      )
    )
  )
## # A tibble: 1 × 4
##   years_of_experience_clean_min years_of_experience_clea…¹ hourly_rate_clean_min
##                           <dbl>                      <dbl>                 <dbl>
## 1                             0                         41                     9
## # ℹ 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_final <- data_final %>%
  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
cat("Rentang Exp Min-Max  :", min(data_final$exp_minmax), "-", max(data_final$exp_minmax), "\n")
## Rentang Exp Min-Max  : 0 - 1
cat("Rentang Rate Min-Max :", min(data_final$rate_minmax), "-", max(data_final$rate_minmax), "\n")
## Rentang Rate Min-Max : 0 - 1

IX.2 Standardisasi Z-Score

Standardisasi Z-Score digunakan untuk mengubah nilai variabel berdasarkan jaraknya terhadap rata-rata dalam satuan standar deviasi. Hasil transformasi ini digunakan untuk melihat posisi suatu nilai relatif terhadap rata-rata datanya. Pada tahap ini, standardisasi dilakukan terhadap variabel years_of_experience_clean dan hourly_rate_clean.

data_final <- data_final %>%
  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
cat("Rata-rata Z-Score Exp  :", round(mean(data_final$exp_z, na.rm = TRUE), 4), "\n")
## Rata-rata Z-Score Exp  : 0
cat("Standar Deviasi Z Exp  :", sd(data_final$exp_z, na.rm = TRUE), "\n")
## Standar Deviasi Z Exp  : 1
cat("Rata-rata Z-Score Rate :", round(mean(data_final$rate_z, na.rm = TRUE), 4), "\n")
## Rata-rata Z-Score Rate : 0
cat("Standar Deviasi Z Rate :", sd(data_final$rate_z, na.rm = TRUE), "\n")
## Standar Deviasi Z Rate : 1

IX.3 Transformasi Invers dan Konsolidasi Data

Transformasi Inverse Square dilakukan untuk menghasilkan bentuk transformasi alternatif pada variabel numerik. Sebelum transformasi, dilakukan pemeriksaan terhadap nilai nol pada years_of_experience_clean. Penambahan nilai 1 digunakan pada rumus transformasi untuk menghindari pembagian dengan nol.

Selain melakukan transformasi, pada bagian ini juga dilakukan visualisasi distribusi pengalaman kerja sebelum transformasi serta konsolidasi hasil seluruh transformasi ke dalam objek data_transformasi. Selanjutnya, ringkasan statistik digunakan untuk mengevaluasi hasil transformasi yang telah dilakukan.

# 1. Pengecekan Keamanan Nilai Nol
data_final %>%
  summarise(
    min_val    = min(years_of_experience_clean, na.rm = TRUE),
    jumlah_nol = sum(years_of_experience_clean == 0, na.rm = TRUE)
  )
## # A tibble: 1 × 2
##   min_val jumlah_nol
##     <dbl>      <int>
## 1       0         72
# 2. Transformasi Inverse Square (+1 Proteksi Pembagian 1/0)
data_final <- data_final %>%
  mutate(
    exp_inv_sq  = 1 / ((years_of_experience_clean + 1)^2),
    rate_inv_sq = 1 / ((hourly_rate_clean + 1)^2)
  )

# 3. Visualisasi Distribusi Sebelum Transformasi Invers
ggplot(data_final, aes(x = years_of_experience_clean)) +
  geom_histogram(bins = 20, fill = "steelblue", color = "white") +
  labs(
    title = "Distribusi Pengalaman Kerja Sebelum Transformasi",
    x = "Pengalaman Kerja (Tahun)",
    y = "Frekuensi"
  ) +
  theme_minimal()

# 4. Konsolidasi & Evaluasi Ringkasan Statistik
data_transformasi <- data_final %>%
  mutate(
    exp_minmax  = min_max(years_of_experience_clean),
    exp_z       = as.numeric(scale(years_of_experience_clean)),
    exp_inv_sq  = 1 / ((years_of_experience_clean + 1)^2),
    rate_minmax = min_max(hourly_rate_clean),
    rate_z      = as.numeric(scale(hourly_rate_clean)),
    rate_inv_sq = 1 / ((hourly_rate_clean + 1)^2)
  )

# 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        = sd(exp_z, na.rm = TRUE),
    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>

Menyimpan Dataset Akhir

nama_file_output <-
  "data_analisis_final.csv"

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

cat(
  "Proses selesai. File disimpan di:\n",
  normalizePath(
    nama_file_output,
    mustWork = FALSE
  ),
  "\n"
)
## Proses selesai. File disimpan di:
##  C:\Users\lenovo\Documents\cool yeah\komstat\projek\data_analisis_final.csv

Memeriksa Dataset Akhir

cat(
  "Dimensi dataset final:\n"
)
## Dimensi dataset final:
print(
  dim(data_analisis)
)
## [1] 1000   10
cat(
  "\nStruktur dataset final:\n"
)
## 
## Struktur dataset final:
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 9 50 ...
##  $ client_satisfaction: num [1:1000] 0.82 0.84 0.71 0.9 0.83 0.82 0.94 0.94 0.76 0.82 ...
##  $ 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" ...
cat(
  "\nMissing value per variabel:\n"
)
## 
## Missing value per variabel:
print(
  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
cat(
  "\nLima baris pertama:\n"
)
## 
## Lima baris pertama:
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>