1. Lakukan pengecekan apakah terdapat: ✓ Nilai yang hilang (missing values) ✓ Data yang tercatat ganda (duplicate records) ✓ Nilai ekstrim (outlier) pada variabel numerik Jika ada masalah tersebut, lakukan penanganan yang sesuai. Jelaskan mengapa menggunakan penanganan tersebut. Setelah pembersihan data, lakukan analisis statistika deskriptif untuk semua variabel numberik.
data <- read.csv("C:/Users/FIOLA/OneDrive/Documents/SEMESTER 4/PSD/DATA UTS PSD A.csv")

# Mengecek missing values
cat("Jumlah missing value per kolom:\n")
## Jumlah missing value per kolom:
missing_values <- colSums(is.na(data))
print(missing_values)
##      age      sex       cp trestbps     chol      fbs  restecg  thalach 
##        0        0        0        0        0        0        0        0 
##    exang  oldpeak    slope       ca     thal   target 
##        0        0        0        0        0        0
if (any(missing_values > 0)) {
  cat("\nTerdapat nilai missing value dalam dataset.\n")
} else {
  cat("\nTidak ditemukan missing value dalam dataset.\n")
}
## 
## Tidak ditemukan missing value dalam dataset.
# Mengecek duplikasi data
cat("\nJumlah data duplikat:\n")
## 
## Jumlah data duplikat:
sum(duplicated(data))
## [1] 1
#menghapus agar analisis tidak bias
data <- data[!duplicated(data), ]

# Deteksi outlier pada variabel numerik
library(ggplot2)
library(dplyr)
## Warning: package 'dplyr' was built under R version 4.4.3
## 
## 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
library(tidyr)
## Warning: package 'tidyr' was built under R version 4.4.3
# Boxplot Sebelum Penanganan Outlier
numeric_cols <- sapply(data, is.numeric)

data_long_before <- data %>%
  pivot_longer(cols = names(data)[numeric_cols],
               names_to = "Variabel", values_to = "Nilai")

ggplot(data_long_before, aes(x = Variabel, y = Nilai, fill = Variabel)) +
  geom_boxplot(outlier.color = "red", outlier.shape = 16) +
  scale_fill_manual(values = heat.colors(length(unique(data_long_before$Variabel)))) +
  labs(title = "Boxplot Sebelum Penanganan Outlier",
       x = "Variabel", y = "Nilai") +
  theme_minimal() +
  theme(axis.text.x = element_text(angle = 45, hjust = 1))

# 2. Penanganan Outlier menggunakan IQR
handle_outliers <- function(x) {
  Q1 <- quantile(x, 0.25, na.rm = TRUE)
  Q3 <- quantile(x, 0.75, na.rm = TRUE)
  IQR <- Q3 - Q1
  lower <- Q1 - 1.5 * IQR
  upper <- Q3 + 1.5 * IQR
  x[x < lower | x > upper] <- NA
  return(x)
}

# Buat salinan data dan bersihkan
data_clean <- data
data_clean[numeric_cols] <- lapply(data_clean[numeric_cols], handle_outliers)

# 3. Boxplot Setelah Penanganan Outlier
data_long_after <- data_clean %>%
  pivot_longer(cols = names(data_clean)[numeric_cols],
               names_to = "Variabel", values_to = "Nilai")

ggplot(data_long_after, aes(x = Variabel, y = Nilai, fill = Variabel)) +
  geom_boxplot(outlier.color = "red", outlier.shape = 16) +
  scale_fill_manual(values = heat.colors(length(unique(data_long_after$Variabel)))) +
  labs(title = "Boxplot Setelah Penanganan Outlier (IQR)",
       x = "Variabel", y = "Nilai") +
  theme_minimal() +
  theme(axis.text.x = element_text(angle = 45, hjust = 1))
## Warning: Removed 91 rows containing non-finite outside the scale range
## (`stat_boxplot()`).

# Statistik Deskriptif Sebelum Penanganan Outlier
cat("==== Statistik Deskriptif Sebelum Penanganan Outlier ====\n")
## ==== Statistik Deskriptif Sebelum Penanganan Outlier ====
summary(data[numeric_cols])
##       age             sex               cp            trestbps    
##  Min.   :29.00   Min.   :0.0000   Min.   :0.0000   Min.   : 94.0  
##  1st Qu.:48.00   1st Qu.:0.0000   1st Qu.:0.0000   1st Qu.:120.0  
##  Median :55.50   Median :1.0000   Median :1.0000   Median :130.0  
##  Mean   :54.42   Mean   :0.6821   Mean   :0.9636   Mean   :131.6  
##  3rd Qu.:61.00   3rd Qu.:1.0000   3rd Qu.:2.0000   3rd Qu.:140.0  
##  Max.   :77.00   Max.   :1.0000   Max.   :3.0000   Max.   :200.0  
##       chol            fbs           restecg          thalach     
##  Min.   :126.0   Min.   :0.000   Min.   :0.0000   Min.   : 71.0  
##  1st Qu.:211.0   1st Qu.:0.000   1st Qu.:0.0000   1st Qu.:133.2  
##  Median :240.5   Median :0.000   Median :1.0000   Median :152.5  
##  Mean   :246.5   Mean   :0.149   Mean   :0.5265   Mean   :149.6  
##  3rd Qu.:274.8   3rd Qu.:0.000   3rd Qu.:1.0000   3rd Qu.:166.0  
##  Max.   :564.0   Max.   :1.000   Max.   :2.0000   Max.   :202.0  
##      exang           oldpeak          slope             ca        
##  Min.   :0.0000   Min.   :0.000   Min.   :0.000   Min.   :0.0000  
##  1st Qu.:0.0000   1st Qu.:0.000   1st Qu.:1.000   1st Qu.:0.0000  
##  Median :0.0000   Median :0.800   Median :1.000   Median :0.0000  
##  Mean   :0.3278   Mean   :1.043   Mean   :1.397   Mean   :0.7185  
##  3rd Qu.:1.0000   3rd Qu.:1.600   3rd Qu.:2.000   3rd Qu.:1.0000  
##  Max.   :1.0000   Max.   :6.200   Max.   :2.000   Max.   :4.0000  
##       thal           target     
##  Min.   :0.000   Min.   :0.000  
##  1st Qu.:2.000   1st Qu.:0.000  
##  Median :2.000   Median :1.000  
##  Mean   :2.315   Mean   :0.543  
##  3rd Qu.:3.000   3rd Qu.:1.000  
##  Max.   :3.000   Max.   :1.000
# Tangani missing value di data_clean setelah outlier diubah jadi NA
# Misalnya isi NA dengan median
for (col in names(data_clean)[numeric_cols]) {
  data_clean[[col]][is.na(data_clean[[col]])] <- median(data_clean[[col]], na.rm = TRUE)
}

# Statistik Deskriptif Setelah Penanganan Outlier
cat("\n==== Statistik Deskriptif Setelah Penanganan Outlier ====\n")
## 
## ==== Statistik Deskriptif Setelah Penanganan Outlier ====
summary(data_clean[numeric_cols])
##       age             sex               cp            trestbps    
##  Min.   :29.00   Min.   :0.0000   Min.   :0.0000   Min.   : 94.0  
##  1st Qu.:48.00   1st Qu.:0.0000   1st Qu.:0.0000   1st Qu.:120.0  
##  Median :55.50   Median :1.0000   Median :1.0000   Median :130.0  
##  Mean   :54.42   Mean   :0.6821   Mean   :0.9636   Mean   :130.1  
##  3rd Qu.:61.00   3rd Qu.:1.0000   3rd Qu.:2.0000   3rd Qu.:140.0  
##  Max.   :77.00   Max.   :1.0000   Max.   :3.0000   Max.   :170.0  
##       chol            fbs       restecg          thalach          exang       
##  Min.   :126.0   Min.   :0   Min.   :0.0000   Min.   : 88.0   Min.   :0.0000  
##  1st Qu.:211.0   1st Qu.:0   1st Qu.:0.0000   1st Qu.:134.5   1st Qu.:0.0000  
##  Median :240.0   Median :0   Median :1.0000   Median :153.0   Median :0.0000  
##  Mean   :243.2   Mean   :0   Mean   :0.5265   Mean   :149.8   Mean   :0.3278  
##  3rd Qu.:272.5   3rd Qu.:0   3rd Qu.:1.0000   3rd Qu.:166.0   3rd Qu.:1.0000  
##  Max.   :360.0   Max.   :0   Max.   :2.0000   Max.   :202.0   Max.   :1.0000  
##     oldpeak           slope             ca              thal      
##  Min.   :0.0000   Min.   :0.000   Min.   :0.0000   Min.   :1.000  
##  1st Qu.:0.0000   1st Qu.:1.000   1st Qu.:0.0000   1st Qu.:2.000  
##  Median :0.7000   Median :1.000   Median :0.0000   Median :2.000  
##  Mean   :0.9732   Mean   :1.397   Mean   :0.4669   Mean   :2.328  
##  3rd Qu.:1.6000   3rd Qu.:2.000   3rd Qu.:1.0000   3rd Qu.:3.000  
##  Max.   :4.0000   Max.   :2.000   Max.   :2.0000   Max.   :3.000  
##      target     
##  Min.   :0.000  
##  1st Qu.:0.000  
##  Median :1.000  
##  Mean   :0.543  
##  3rd Qu.:1.000  
##  Max.   :1.000

Dapat dilihat diatas, pada data yang digunakan tidak terdapat missing value. Dan data yang tercatat ganda atau data duplikat ada satu. Dan terdapat nilai outlier pada beberapa variabel, dengan variabel “chol” memiliki nilai outlier tertinggi. Penanganan outlier penting untuk meningkatkan akurasi analisis dan visualisasi data. Outlier dapat merusak statistik deskriptif dan memengaruhi hasil model.

  1. Lakukan visualisasi untuk menunjukkan jenis kelamin apa yang paling banyak mengalami Heart desease, dan usia berapa yang paling banyak mengalami heart desease, dan usia berapa yang memiliki gula darah lebih besar dari 120 mg/dl.
data <- read.csv("C:/Users/FIOLA/OneDrive/Documents/SEMESTER 4/PSD/DATA UTS PSD A.csv")

library(ggplot2)
library(dplyr)

# Plot 1: Jenis kelamin paling banyak mengalami penyakit jantung
sex_counts <- data %>%
  filter(target == 1) %>%
  count(sex) %>%
  mutate(sex = ifelse(sex == 0, "Perempuan", "Laki-laki"))

ggplot(sex_counts, aes(x = sex, y = n, fill = sex)) +
  geom_bar(stat = "identity") +
  geom_text(aes(label = n), vjust = -0.3, size = 5) +
  scale_fill_manual(values = c("Perempuan" = "#EE2C2C", "Laki-laki" = "#1874CD")) +
  labs(title = "Penderita Penyakit Jantung Berdasarkan Jenis Kelamin",
       x = "Jenis Kelamin", y = "Jumlah") +
  theme_minimal()

# Plot 2: Usia yang paling banyak mengalami penyakit jantung
ggplot(data %>% filter(target == 1), aes(x = age)) +
  geom_histogram(binwidth = 5, fill = "#CDC0B0", color = "black") +
     stat_bin(binwidth = 5, geom = "text", aes(label = ..count..), 
           vjust = -0.3, color = "black") +
  labs(title = "Distribusi Usia Penderita Penyakit Jantung",
       x = "Usia", y = "Jumlah")
## Warning: The dot-dot notation (`..count..`) was deprecated in ggplot2 3.4.0.
## ℹ Please use `after_stat(count)` instead.
## This warning is displayed once every 8 hours.
## Call `lifecycle::last_lifecycle_warnings()` to see where this warning was
## generated.

# Plot 3: Usia dengan gula darah > 120 mg/dl (fbs == 1)
ggplot(data %>% filter(fbs == 1), aes(x = age)) +
  geom_histogram(binwidth = 5, fill = "#8B8378", color = "black") +
   stat_bin(binwidth = 5, geom = "text", aes(label = ..count..), 
           vjust = -0.3, color = "black") +
  labs(title = "Usia dengan Gula Darah > 120 mg/dl",
       x = "Usia", y = "Jumlah")

Terlihat pada visualisasi diatas, jenis kelamin yang paling banyak mengalami Heart desease adalah Laki-laki yang berjumlah 93 orang. Usia yang paling banyak mengalami heart desease adalah pada usia 55 tahun dengan jumlah orag 51, disusul dengan usia 50 tahun. Dan usia yang memiliki gula darah lebih besar dari 120 mg/dl adalah usia 60 tahun dengan jumlah 12 orang dan merupakan yang terbanyak.

  1. Lakukan analisis untuk mengetahui, apakah kadar gula darah seseorang yang lebih besar dari 120 paling banyak mengalami Heart desease?
data <- read.csv("C:/Users/FIOLA/OneDrive/Documents/SEMESTER 4/PSD/DATA UTS PSD A.csv")

# 1. Baca data
data <- read.csv("DATA UTS PSD A.csv")

# 2. Cek struktur untuk memastikan fbs dan target ada
str(data)
## 'data.frame':    303 obs. of  14 variables:
##  $ age     : int  63 37 41 56 57 57 56 44 52 57 ...
##  $ sex     : int  1 1 0 1 0 1 0 1 1 1 ...
##  $ cp      : int  3 2 1 1 0 0 1 1 2 2 ...
##  $ trestbps: int  145 130 130 120 120 140 140 120 172 150 ...
##  $ chol    : int  233 250 204 236 354 192 294 263 199 168 ...
##  $ fbs     : int  1 0 0 0 0 0 0 0 1 0 ...
##  $ restecg : int  0 1 0 1 1 1 0 1 1 1 ...
##  $ thalach : int  150 187 172 178 163 148 153 173 162 174 ...
##  $ exang   : int  0 0 0 0 1 0 0 0 0 0 ...
##  $ oldpeak : num  2.3 3.5 1.4 0.8 0.6 0.4 1.3 0 0.5 1.6 ...
##  $ slope   : int  0 0 2 2 2 1 1 2 2 2 ...
##  $ ca      : int  0 0 0 0 0 0 0 0 0 0 ...
##  $ thal    : int  1 2 2 2 2 1 2 3 3 2 ...
##  $ target  : int  1 1 1 1 1 1 1 1 1 1 ...
# 3. Hapus baris yang NA di fbs dan target
data_fbs <- na.omit(data[, c("fbs", "target")])

# 4. Cek panjang data untuk memastikan sama
cat("Jumlah baris fbs:", length(data_fbs$fbs), "\n")
## Jumlah baris fbs: 303
cat("Jumlah baris target:", length(data_fbs$target), "\n")
## Jumlah baris target: 303
# 5. Buat tabel silang
table_fbs <- table(data_fbs$fbs, data_fbs$target)
print("Tabel silang fbs dan target:")
## [1] "Tabel silang fbs dan target:"
print(table_fbs)
##    
##       0   1
##   0 116 142
##   1  22  23
# 6. Hitung proporsi
proporsi <- prop.table(table_fbs, margin = 1) * 100
print("Proporsi (%):")
## [1] "Proporsi (%):"
print(round(proporsi, 2))
##    
##         0     1
##   0 44.96 55.04
##   1 48.89 51.11
# 7. Visualisasi barplot
barplot(proporsi,
        beside = FALSE,
        legend.text = c("Tidak Mengalami Heart Desease", "Mengalami Heart Desease"),
        col = c("skyblue", "#698B69"),
        main = "Proporsi Penyakit Jantung Berdasarkan Kadar Gula Darah",
        names.arg = c("fbs ≤ 120", "fbs > 120"),
        ylab = "Persentase (%)")

Orang dengan kadar gula darah lebih dari 120 mg/dl lebih banyak mengalami penyakit jantung dibandingkan dengan mereka yang memiliki gula darah kirang dari 120 mg/dl. Maka, kadar gula darah tinggi dapat dianggap sebagai faktor risiko penyakit jantung.

  1. Lakukan identifikasi untuk mengetahui jenis nyeri dada yang paling banyak terjadi pada seseorang mengalami Heart desease
library(ggplot2)

ggplot(data %>% filter(target == 1), aes(x = factor(cp))) +
  geom_bar(fill = "#CD8C95") +
  labs(title = "Jenis Nyeri Dada pada Penderita Penyakit Jantung",
       x = "Tipe Nyeri Dada (cp)",
       y = "Jumlah")

Tipe nyeri dada yang paling banyak dialami pada seseorang mengalami Heart desease adalah nyeri dada tipe 2 dengan jumlah 60 lebih.

  1. Buat visualisasi Bar plot proporsi heart desease terhadap variabel kategorikal cp, ca, dan thal. Interpretasikan.
data <- read.csv("C:/Users/FIOLA/OneDrive/Documents/SEMESTER 4/PSD/DATA UTS PSD A.csv")

library(ggplot2)
library(scales)

# Ganti nama kolom "HeartDisease" menjadi "target" jika ada
names(data)[names(data) == "HeartDisease"] <- "target"

# Loop untuk variabel kategorikal: cp, ca, dan thal
for (var in c("cp", "ca", "thal")) {
  p <- ggplot(data, aes_string(x = var, fill = "factor(target)")) +
    geom_bar(position = "fill") +  # untuk proporsi
    scale_y_continuous(labels = percent) +
    scale_fill_manual(values = c("0" = "#BBFFFF", "1" = "#6C7B8B")) +
    labs(title = paste("Proporsi Penyakit Jantung berdasarkan", var),
         x = var,
         y = "Proporsi",
         fill = "Target (1=Sakit, 0=Tidak)") +
    theme_minimal()
  print(p)  
}
## Warning: `aes_string()` was deprecated in ggplot2 3.0.0.
## ℹ Please use tidy evaluation idioms with `aes()`.
## ℹ See also `vignette("ggplot2-in-packages")` for more information.
## This warning is displayed once every 8 hours.
## Call `lifecycle::last_lifecycle_warnings()` to see where this warning was
## generated.

Jenis nyeri dada tipe cp = 1 atau cp = 2, menunjukkan proporsi penderita penyakit jantung yang lebih tinggi dibandingkan jenis lainnya. Ini menunjukkan bahwa jenis nyeri dada dapat menjadi indikator penting dalam mendeteksi risiko penyakit jantung. Nilai ca = 0 terlihat memiliki proporsi terbesar penderita penyakit jantung, sedangkan nilai ca = 2 atau ca = 3 mungkin memiliki lebih banyak yang tidak sakit. Ini bisa mencerminkan bahwa semakin banyak pembuluh darah yang dapat dilihat (tidak tersumbat), semakin kecil risikonya. Nilai tertentu seperti thal = 2 atau thal = 0 bisa menunjukkan kecenderungan lebih tinggi terhadap penyakit jantung.

  1. Lakukan eksplorasi untuk mengetahui arah hubungan antara variabel age dan thalach yang dibedakan untuk seseorang yang memiliki penyakit jantung dan tidak. Apakah hubungan itu linear atau non-linear? Bagaimana cara mengetahuinya?
data <- read.csv("C:/Users/FIOLA/OneDrive/Documents/SEMESTER 4/PSD/DATA UTS PSD A.csv")

ggplot(data, aes(x = age, y = thalach, color = factor(target))) +
  geom_point(alpha = 0.6) +
  geom_smooth(method = "loess", se = FALSE) +
  labs(title = "Hubungan Age vs Thalach",
       x = "Usia", y = "Denyut Jantung Maksimum",
       color = "Target") +
  theme_minimal()
## `geom_smooth()` using formula = 'y ~ x'

Dari visualisasi diatas, dapat disimpulkan bahwa usia dan thalach memiliki hubungan negatif. Individu lebih muda cenderung memiliki denyut jantung maksimum yang lebih tinggi dan lebih jarang mengalami penyakit jantung. Thalach rendah pada usia tua bisa menjadi indikator risiko penyakit jantung.

  1. Buat heatmap korelasi antar variabel yang berskala interval atau rasio kemudian interpretasukan hasil yang didapat
# Load library
library(corrplot)
## Warning: package 'corrplot' was built under R version 4.4.3
## corrplot 0.95 loaded
library(dplyr)

# Pilih variabel berskala interval/rasio
# Misalnya: age, trestbps, chol, thalach, oldpeak
data_num <- data %>% select(age, trestbps, chol, thalach, oldpeak)

# Hitung korelasi
cor_matrix <- cor(data_num)

# Plot heatmap korelasi
corrplot(cor_matrix, method = "color", 
         type = "upper", 
         addCoef.col = "black", # Menampilkan nilai korelasi
         tl.col = "black", tl.srt = 45,
         col = colorRampPalette(c("#FFC0CB", "white", "#8B0000"))(200),
         number.cex = 0.7,
         mar = c(0,0,1,0),
         title = "Heatmap Korelasi Variabel Numerik")
## Warning in ind1:ind2: numerical expression has 2 elements: only the first used

Pada heatmap korelasi diatas, terlihat hubungan antara age dan trestbph sebesar 0.28, hubungan antara chol dan age sebesar 0.21, hubungan antara thalach dan age sebesar -0.40, hubungan oldpeak dengan age sebesar 0.21, hubungan antara chol dengan trestbps sebesar 0.12, hubungan antara thalach dengan trestbps sebesar -0.05, hubungan antara oldpeak dengan trestbps sebesar 0.19, hubungan antara thalach dengan chol sebesar -0.01, hubungan antara oldpeak dengan thalach sebesar -0.34. Yang dimana semua hubungan memiliki nilai yang kecil jauh dibawah 1, korelasi terkecil adalah hubungan antara thalach dan age yaitu sebesar -0.40, dan korelasi terbesar ada diantara trestbps dan age.

library(ggplot2)

data <- data.frame(
  Konsumsi_GWh = c(10,20,30,50,70,90,40,80,120,200,300,400,15,25,35,50,70,5,10,15,20,25,30,40),
  Biaya_per_kWh = c(1500,1450,1400,1350,1330,1250,1350,1250,1250,1150,1150,1050,1600,1550,1500,1450,1400,1700,1600,1550,1500,1450,1400,1350),
  Konsumen = c(rep("Rumah Tangga",6), rep("Industri",6), rep("Kantor Pemerintah",6), rep("UMKM",6))
)

ggplot(data, aes(x = Konsumsi_GWh, y = Biaya_per_kWh, color = Konsumen)) +
  geom_point(size = 3) +
  geom_smooth(method = "lm", se = FALSE, linetype = "dashed") +
  scale_y_continuous(limits = c(1000, 1800), breaks = seq(1000,1800,100)) +
  scale_x_log10() +
    scale_color_manual(values = c(
    "Rumah Tangga" = "#8B8378",      
    "Industri" = "#458B74",          
    "Kantor Pemerintah" = "#104E8B",
    "UMKM" = "#AF7AC5"               
  )) +
  labs(title = "Biaya Listrik per kWh Menurun Seiring Kenaikan Konsumsi",
       x = "Total Konsumsi Listrik (GWh)",
       y = "Biaya per kWh (Rupiah)") +
  theme_minimal()
## `geom_smooth()` using formula = 'y ~ x'

Semua segmen konsumen menunjukkan pola ekonomi skala, konsumsi lebih besar menyebabkan biaya per kWh menjadi lebih murah. Industri dan Rumah Tangga terlihat mendapat tarif per kWh yang lebih murah dibandingkan kelompok lainnya. UMKM dan Kantor Pemerintah cenderung tetap membayar lebih mahal per kWh, terutama di konsumsi yang lebih rendah. Hal ini mencerminkan struktur tarif listrik yang bersifat progresif atau diskriminatif berdasarkan volume konsumsi dan jenis pelanggan.