Preprocess

# Memuat library dplyr untuk manipulasi data
library(dplyr)
## 
## Attaching package: 'dplyr'
## The following objects are masked from 'package:stats':
## 
##     filter, lag
## The following objects are masked from 'package:base':
## 
##     intersect, setdiff, setequal, union
# Memuat library ggplot2 untuk visualisasi data
library(ggplot2)

# Memuat dataset starwars yang sudah tersedia dalam paket dplyr
data(starwars)

# Menghapus baris yang mengandung nilai NA (missing values) dari dataset starwars
starwars_data <- starwars %>% 
  na.omit()

# Menyimpan kolom 'height' dari starwars_data ke dalam variabel height_data
height_data <- starwars_data$height

# Menyimpan kolom 'mass' dari starwars_data ke dalam variabel mass_data
mass_data <- starwars_data$mass

# Membuat dataframe baru bernama data1 yang berisi kolom 'height' dan 'mass' dari starwars_data
data1 <- data.frame(height = starwars_data$height, mass = starwars_data$mass)

Nomor 1

Eksplorasi Data

Mengecek Struktur Data

str(height_data)
##  int [1:29] 172 202 150 178 165 183 182 188 228 180 ...
str(mass_data)
##  num [1:29] 77 136 49 120 75 84 77 84 112 80 ...

Ringkasan Statistik Data untuk Height dan Mass

summary(height_data)
##    Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
##    88.0   172.0   180.0   178.7   188.0   228.0
summary(mass_data)
##    Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
##   20.00   75.00   79.00   77.77   83.00  136.00

Histogram untuk Height

ggplot(starwars_data, aes(x = height)) +   # Menggunakan data 'starwars_data' dan menentukan kolom 'height' untuk sumbu X
  geom_histogram(bins = 15, fill = "blue", color = "black") +   # Membuat histogram dengan 15 bin, warna isian biru, dan warna batas hitam
  ggtitle("Distribusi Height pada Dataset Starwars") +  # Memberikan judul pada plot
  xlab("Height") +   # Menambahkan label pada sumbu X
  ylab("Frekuensi")   # Menambahkan label pada sumbu Y

Histogram distribusi height pada dataset Star Wars menunjukkan bahwa mayoritas karakter memiliki tinggi antara 160 hingga 200 cm, dengan puncak frekuensi tertinggi berada di sekitar 170–180 cm. Terdapat beberapa karakter dengan tinggi ekstrem, baik yang sangat pendek (sekitar 80 cm) maupun sangat tinggi (mendekati 240 cm), yang kemungkinan merupakan outlier.

Histogram untuk Mass

ggplot(starwars_data, aes(x = mass)) +   # Menggunakan data 'starwars_data' dan menentukan kolom 'mass' untuk sumbu X
  geom_histogram(bins = 15, fill = "green", color = "black") +   # Membuat histogram dengan 15 bin, warna isian hijau, dan warna batas hitam
  ggtitle("Distribusi Mass pada Dataset Starwars") +  # Memberikan judul pada plot
  xlab("Mass") +   # Menambahkan label pada sumbu X
  ylab("Frekuensi")   # Menambahkan label pada sumbu Y

Histogram distribusi mass pada dataset Star Wars menunjukkan bahwa mayoritas karakter memiliki massa tubuh antara 75 hingga 100 kg, dengan puncak frekuensi tertinggi berada di sekitar 85–90 kg. Distribusi ini tampak menyebar luas, dengan beberapa karakter memiliki massa yang jauh lebih kecil (di bawah 30 kg) maupun jauh lebih besar (di atas 100 kg), mengindikasikan keberadaan outlier.

Nomor 2 (Mencari Pencilan)

Teknik Visual

Histogram

hist(height_data,       # Menggunakan data tinggi karakter
     main="Distribusi Height",  # Menambahkan judul histogram
     xlab="Height",     # Label untuk sumbu X
     col="orange",      # Warna batang histogram
     border="black",    # Warna batas setiap batang histogram
     breaks=20)        # Membagi histogram menjadi 20 interval (bin)

hist(mass_data,        # Menggunakan data massa karakter
     main="Distribusi Mass",  # Menambahkan judul histogram
     xlab="Mass",      # Label untuk sumbu X
     col="yellow",     # Warna batang histogram
     border="black",   # Warna batas setiap batang histogram
     breaks=20)       # Membagi histogram menjadi 20 interval (bin)

### Boxplot

boxplot(height_data,    # Menggunakan data tinggi karakter
        main="Boxplot Height", # Menambahkan judul boxplot
        ylab="Height",  # Label untuk sumbu Y
        col="lightblue", # Warna boxplot
        border="black")  # Warna batas boxplot

boxplot(mass_data,      # Menggunakan data massa karakter
        main="Boxplot Mass",  # Menambahkan judul boxplot
        ylab="Mass",    # Label untuk sumbu Y
        col="lightgreen", # Warna boxplot
        border="black")  # Warna batas boxplot

### Scatterplot

plot(height_data, mass_data,  # Menggunakan data tinggi sebagai X dan massa sebagai Y
     main="Scatterplot: Height vs Mass",  # Menambahkan judul scatterplot
     xlab="Height",   # Label untuk sumbu X
     ylab="Mass",     # Label untuk sumbu Y
     pch=19,         # Tipe titik (19 = bulatan penuh)
     col="darkblue") # Warna titik scatterplot

### Mencari Pencilan

Cara 1 (height) - Tukey’s Fence

IQR.hwy = IQR(height_data)  # Menghitung Interquartile Range (IQR) dari height_data

Q1.height = quantile(height_data, 0.25)  # Kuartil pertama (Q1)
Q3.height = quantile(height_data, 0.75)  # Kuartil ketiga (Q3)

batas.bawah = Q1.height - 1.5*IQR.hwy  # Menentukan batas bawah pencilan
batas.atas = Q3.height + 1.5*IQR.hwy  # Menentukan batas atas pencilan

nilai_pencilan = height_data[height_data < batas.bawah | height_data > batas.atas] # Menentukan nilai yang berada di luar batas bawah dan atas (outlier)

nilai_pencilan  # Menampilkan nilai pencilan
## [1] 228  88
lokasi_pencilan_pada_urut_ke = which(height_data < batas.bawah | height_data > batas.atas)# Mencari indeks dari nilai yang merupakan pencilan

lokasi_pencilan_pada_urut_ke  # Menampilkan indeks dari pencilan
## [1]  9 18

Cara 2

boxplot.stats(height_data)$out  # Menampilkan nilai outlier langsung dari fungsi boxplot
## [1] 228  88
pencilan = boxplot.stats(height_data)$out  # Menyimpan nilai pencilan
lokasi_pencilan = which(height_data %in% c(pencilan))  # Mencari lokasi indeks dari pencilan
lokasi_pencilan  # Menampilkan indeks pencilan
## [1]  9 18
boxplot(height_data,   # Membuat boxplot untuk tinggi
        xlab = "height",  # Label sumbu X
        ylab = "Value",   # Label sumbu Y
        main = "Boxplot Height"  # Judul boxplot
)

mtext(paste("Pencilan: ", paste(pencilan, collapse = ", "))) # Menampilkan teks di bawah plot dengan nilai pencilan yang ditemukan

#### Cara 1 (Mass) - Tukey’s Fence

IQR.mass = IQR(mass_data)

Q1.mass = quantile(mass_data, 0.25)
Q3.mass = quantile(mass_data, 0.75)

batas.bawah = Q1.mass - 1.5*IQR.mass
batas.atas = Q3.mass + 1.5*IQR.mass

nilai_pencilan_mass = mass_data[mass_data < batas.bawah | mass_data > batas.atas]
nilai_pencilan_mass
##  [1] 136.0  49.0 120.0 112.0 113.0  20.0  45.0  55.0  56.2  50.0
lokasi_pencilan_mass_pada_urut_ke = which(mass_data < batas.bawah | mass_data > batas.atas)
lokasi_pencilan_mass_pada_urut_ke
##  [1]  2  3  4  9 14 18 19 22 26 27

Cara 2

boxplot.stats(mass_data)$out
##  [1] 136.0  49.0 120.0 112.0 113.0  20.0  45.0  55.0  56.2  50.0
pencilann =  boxplot.stats(mass_data)$out
lokasi_pencilan_mass = which(mass_data %in% c(pencilann))
lokasi_pencilan_mass
##  [1]  2  3  4  9 14 18 19 22 26 27
boxplot(mass_data,
        xlab = "mass",
        ylab = "Value",
        main = "Boxplot Mass"
)
mtext(paste("Pencilan: ", paste(pencilann, collapse = ", ")))

Nomor 3

Untuk data Mass, terdapat pencilan yang cukup jauh dari nilai tengah dan cukup banyak pencilan yang ada. Trimmed Mean mungkin menjadi pilihan yang lebih baik di sini, karena metode ini akan memotong sebagian kecil dari data ekstrim di kedua ujung distribusi (misalnya, 15% dari data terendah dan tertinggi). Dengan cara ini, kita bisa mendapatkan nilai rata-rata yang lebih representatif, karena nilai ekstrim yang bisa merusak analisis akan dihapus.

Namun, jika kita menggunakan Winsorized Mean, kita tidak membuang data ekstrim, melainkan menggantinya dengan nilai batas terdekat dalam distribusi. Meskipun ini juga berguna untuk menangani pencilan, Trimmed Mean lebih cocok pada kasus ini karena pencilan pada data Mass cukup jauh, serta pencilannya juga banyak, sehingga mengurangi data lebih efektif daripada menggantinya.

Sementara itu, untuk data Height, pencilannya tidak begitu ekstrem. Dalam kasus ini, menggunakan Winsorized Mean bisa lebih tepat, karena pencilan tidak terlalu besar dan menggantinya dengan batas terdekat bisa memberikan estimasi yang lebih stabil tanpa menghilangkan data.

Data Height (Trimmed Mean)

rataan.heightbiasa = mean(height_data)
rataan.heightterpangkas = mean(height_data, trim = 0.15)

cat("Rataan height Biasa: ", rataan.heightbiasa, "\n")
## Rataan height Biasa:  178.6552
cat("Rataan height Terpangkas : ", rataan.heightterpangkas)
## Rataan height Terpangkas :  180.381

Data Mass (Winsorized Mean)

library(datawizard)
## Warning: package 'datawizard' was built under R version 4.4.3
cat("Sebelum : ",mass_data)
## Sebelum :  77 136 49 120 75 84 77 84 112 80 77 75 78.2 113 79 79 83 20 45 66 80 55 84 82 80 56.2 50 80 79
win.mass_data=winsorize(
  mass_data,
  threshold = 0.15,
  method = "percentile",
  robust = FALSE,
  verbose = TRUE)
cat("\nSesudah : ",win.mass_data)
## 
## Sesudah :  77 84 55 84 75 84 77 84 84 80 77 75 78.2 84 79 79 83 55 55 66 80 55 84 82 80 56.2 55 80 79
cat("\nWinsorized Mean dari mass_data : ",mean(winsorize(win.mass_data)))
## 
## Winsorized Mean dari mass_data :  74.91034

Nomor 4

data <- rbind(data1, data.frame(height = 210, mass = 100)) 
data
##    height  mass
## 1     172  77.0
## 2     202 136.0
## 3     150  49.0
## 4     178 120.0
## 5     165  75.0
## 6     183  84.0
## 7     182  77.0
## 8     188  84.0
## 9     228 112.0
## 10    180  80.0
## 11    170  77.0
## 12    170  75.0
## 13    183  78.2
## 14    190 113.0
## 15    177  79.0
## 16    175  79.0
## 17    180  83.0
## 18     88  20.0
## 19    185  45.0
## 20    196  66.0
## 21    175  80.0
## 22    178  55.0
## 23    188  84.0
## 24    198  82.0
## 25    188  80.0
## 26    170  56.2
## 27    166  50.0
## 28    193  80.0
## 29    183  79.0
## 30    210 100.0
iqr_height <- IQR(data$height)
iqr_mass <- IQR(data$mass)

q1_height <- quantile(data$height, 0.25)
q3_height <- quantile(data$height, 0.75)

q1_mass <- quantile(data$mass, 0.25)
q3_mass <- quantile(data$mass, 0.75)

lower_bound_height <- q1_height - 1.5 * iqr_height
upper_bound_height <- q3_height + 1.5 * iqr_height

lower_bound_mass <- q1_mass - 1.5 * iqr_mass
upper_bound_mass <- q3_mass + 1.5 * iqr_mass

height_outlier <- 210 < lower_bound_height | 210 > upper_bound_height
mass_outlier <- 100 < lower_bound_mass | 100 > upper_bound_mass

cat("210 termasuk pencilan pada height? ", height_outlier , "\n")
## 210 termasuk pencilan pada height?  FALSE
cat("100 termasuk pencilan pada mass? ", mass_outlier, "\n")
## 100 termasuk pencilan pada mass?  TRUE
Kesimpulannya adalah 210 bukan merupakan outlier pada data height, sedangkan 100 merupakan outlier pada data mass.