1. Data heart desease

Link data https://www.kaggle.com/code/farzadnekouei/heart-disease-prediction/input

Penjelasan variabel dan skala data :

a). Pengecekan data

# Baca file heart.csv
data <- read.csv("D:\\KAMPUS MBA\\PSD\\heart.csv")

# Jumlah nilai yang hilang di setiap kolom
colSums(is.na(data))
##      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
data$chol[is.na(data$chol)] <- median(data$chol, na.rm = TRUE)

# Cek jumlah baris duplikat
sum(duplicated(data))
## [1] 1
#Penangan duplikat
data <- data[!duplicated(data), ]

# Hapus data duplikat
data <- data[!duplicated(data), ]

# Buat boxplot untuk semua variabel numerik
num_vars <- sapply(data, is.numeric)
boxplot(data[, num_vars], main = "Boxplot Variabel Numerik", las = 2, col = "rosybrown1")

data <- subset(data, chol <= 600)

# Statistika deskriptif
summary(data[, num_vars])
##       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

Tidak ditemukan missing values pada seluruh kolom data, semua nilai dalam dataset telah terisi lengkap sehingga tidak diperlukan penanganan terhadap nilai hilang. Ditemukan 1 baris duplikat. Data duplikat dapat menyebabkan bias dalam analisis karena mengulang informasi yang sama. Menghapus duplikat membuat data lebih akurat dan representatif. Kemudian, untuk penanganan outlier yang sangat ekstrem dan tidak masuk akal bisa dihapus menggunakan metode IQR. Namun, dalam data ini tidak dihapus karena nilai-nilainya masih memungkinkan secara medis.

b). Visualisasi Bar Chart

library(ggplot2)
## Warning: package 'ggplot2' was built under R version 4.3.3
ggplot(data[data$target == 1, ], aes(x = factor(sex), fill = factor(sex))) +
  geom_bar() +
  scale_fill_manual(values = c("0" = "rosybrown1", "1" = "rosybrown"),
                    labels = c("0" = "Perempuan", "1" = "Laki-laki")) +
  labs(title = "Jumlah Penderita Penyakit Jantung Berdasarkan Jenis Kelamin",
       x = "Jenis Kelamin (0 = Perempuan, 1 = Laki-laki)",
       y = "Jumlah Pasien",
       fill = "Jenis Kelamin")

ggplot(data[data$target == 1, ], aes(x = age)) +
  geom_histogram(binwidth = 1, fill = "rosybrown3", color = "black") +
  labs(title = "Distribusi Usia Penderita Penyakit Jantung",
       x = "Usia",
       y = "Jumlah Pasien")

ggplot(data[data$fbs == 1, ], aes(x = age)) +
  geom_histogram(binwidth = 1, fill = "rosybrown", color = "black") +
  labs(title = "Distribusi Usia dengan Gula Darah > 120 mg/dl",
       x = "Usia",
       y = "Jumlah Pasien")

Jenis kelamin yang paling banyak mengalami Heart desease adalah laki-laki, usia yang paling banyak mengalami heart desease adalah sekitar 50 sampai 55 tahun, dengan puncaknya di sekitar usia 54 sampai 55 tahun, dan usia yang memiliki gula darah lebih besar dari 120 mg/dl adalah sekitar 51 smapai 52 tahun dengan jumlah pasien terbanyak mencapai 5 orang.

c). Analisis

# Tabel kontingensi antara fbs dan target (penyakit jantung)
table_fbs_target <- table(data$fbs, data$target)
colnames(table_fbs_target) <- c("Tidak_Sakit_Jantung", "Sakit_Jantung")
rownames(table_fbs_target) <- c("Gula_Darah_Normal", "Gula_Darah_Tinggi")
print(table_fbs_target)
##                    
##                     Tidak_Sakit_Jantung Sakit_Jantung
##   Gula_Darah_Normal                 116           141
##   Gula_Darah_Tinggi                  22            23
# Proporsi masing-masing baris (per kategori fbs)
prop.table(table_fbs_target, 1)
##                    
##                     Tidak_Sakit_Jantung Sakit_Jantung
##   Gula_Darah_Normal           0.4513619     0.5486381
##   Gula_Darah_Tinggi           0.4888889     0.5111111
# Uji chi square
chisq.test(table_fbs_target)
## 
##  Pearson's Chi-squared test with Yates' continuity correction
## 
## data:  table_fbs_target
## X-squared = 0.092408, df = 1, p-value = 0.7611

Berdasarkan analisis data dan uji chi-square menyatakan bahwa tidak terdapat hubungan yang signifikan antara kadar gula darah >120 dengan penyakit jantung (p-value = 0,7611). Meskipun secara persentase penderita penyakit jantung sedikit lebih tinggi pada kelompok dengan gula darah tinggi sebesar 51,1% dibandingkan dengan yang normal sebesar 54,9%, perbedaan ini tidak signifikan. Bahkan penderita penyakit jantung lebih banyak berasal dari kelompok dengan gula darah normal karena ukuran kelompoknya lebih besar. Oleh karena itu, kadar gula darah >120 tidak dapat disimpulkan sebagai faktor yang paling banyak berhubungan dengan penyakit jantung dalam data ini.

d). Identifikasi

# Buat tabel kontingensi antara cp dan target
table_cp_target <- table(data$cp, data$target)

# Ambil hanya baris dengan target = 1 (artinya sakit jantung)
table_cp_target[, "1"]
##  0  1  2  3 
## 39 41 68 16

Berdasarkan hasil analisis data menyatakan bahwa jenis nyeri dada yang paling banyak dialami oleh penderita penyakit jantung (target = 1) adalah Non-anginal Pain (cp = 2) dengan jumlah kasus sebanyak 68 orang, artinya banyak penderita penyakit jantung tidak mengalami nyeri dada khas seperti angina, melainkan nyeri yang tidak spesifik.

e). Visualisasi Bar Plot

library(dplyr)
## Warning: package 'dplyr' was built under R version 4.3.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
# Visualisasi proporsi berdasarkan cp (Chest Pain Type)
p1 <- data %>%
  group_by(cp, target) %>%
  summarise(count = n(), .groups = "drop") %>%
  mutate(prop = count / sum(count)) %>%
  ggplot(aes(x = factor(cp), y = prop, fill = factor(target))) +
  geom_bar(stat = "identity", position = "dodge") +
  scale_fill_manual(values = c("0" = "rosybrown1", "1" = "rosybrown3")) +
  labs(title = "Proporsi Heart Disease Berdasarkan Chest Pain (cp)",
       x = "Chest Pain Type (cp)",
       y = "Proporsi",
       fill = "Heart Disease (target)") +
  theme_minimal()

# Visualisasi proporsi berdasarkan ca (Jumlah Pembuluh)
p2 <- data %>%
  group_by(ca, target) %>%
  summarise(count = n(), .groups = "drop") %>%
  mutate(prop = count / sum(count)) %>%
  ggplot(aes(x = factor(ca), y = prop, fill = factor(target))) +
  geom_bar(stat = "identity", position = "dodge") +
  scale_fill_manual(values = c("0" = "rosybrown1", "1" = "rosybrown3")) +
  labs(title = "Proporsi Heart Disease Berdasarkan Jumlah Pembuluh (ca)",
       x = "Number of Major Vessels (ca)",
       y = "Proporsi",
       fill = "Heart Disease (target)") +
  theme_minimal()

# Visualisasi proporsi berdasarkan thal (Thalassemia Type)
p3 <- data %>%
  group_by(thal, target) %>%
  summarise(count = n(), .groups = "drop") %>%
  mutate(prop = count / sum(count)) %>%
  ggplot(aes(x = factor(thal), y = prop, fill = factor(target))) +
  geom_bar(stat = "identity", position = "dodge") +
  scale_fill_manual(values = c("0" = "rosybrown1", "1" = "rosybrown3")) +
  labs(title = "Proporsi Heart Disease Berdasarkan Thalassemia (thal)",
       x = "Thalassemia Type (thal)",
       y = "Proporsi",
       fill = "Heart Disease (target)") +
  theme_minimal()

# Tampilkan grafik satu per satu
p1

p2

p3

Pada variabel cp, proporsi penderita penyakit jantung paling tinggi di tipe nyeri dada 2 sebesar 77% dan terendah di tipe nyeri dada 0 sebesar 42,9% sehingga tidak monotonic karena semakin berat nyeri tidak berarti semakin tinggi risiko. Kemudian, pada variabel ca, individu dengan jumlah pembuluh darah besar bernilai 0 sebesar 72,4% menunjukkan proporsi tertinggi menderita penyakit jantung dan proporsi tersebut menurun seiring bertambahnya nilai ca sehingga makin besar nilai ca maka makin kecil risiko. Sedangkan pada variabel thal, proporsi penyakit jantung paling tinggi terdapat pada tipe thalassemia 2, sedangkan tipe 3 didominasi oleh individu yang tidak menderita penyakit jantung sehingga tidak monotonic karena urutan risikonya tidak sesuai dengan urutan angkanya.

f). Eksplorasi

# Ubah target ke faktor agar mudah dibedakan
data$target <- factor(data$target, labels = c("Tidak Sakit", "Sakit"))

# Scatter plot
ggplot(data, aes(x = age, y = thalach, color = target)) +
  geom_point(alpha = 0.6) +
  geom_smooth(method = "loess", se = FALSE, linetype = "solid") +  # untuk lihat pola non-linear
  geom_smooth(method = "lm", se = FALSE, linetype = "dashed") +   # untuk lihat pola linear
   scale_color_manual(values = c("Tidak Sakit" = "rosybrown1", "Sakit" = "rosybrown")) +
  labs(title = "Hubungan Age dan Thalach Berdasarkan Status Penyakit Jantung",
       x = "Umur (Age)",
       y = "Denyut Jantung Maksimal (Thalach)",
       color = "Status Jantung") +
  theme_minimal()
## `geom_smooth()` using formula = 'y ~ x'
## `geom_smooth()` using formula = 'y ~ x'

Untuk mengetahui arah hubungan antara variabel age dan thalach yang dibedakan untuk seseorang yang memiliki penyakit jantung dan tidak dilakukan dengan cara menbuat scatter plot. Hasilnya menunjukkan hubungan negatif antara umur dan denyut jantung maksimal (thalach) yang dibedakan berdasarkan status penyakit jantung. Terlihat bahwa baik pada individu yang sakit maupun tidak sakit jantung, nilai thalach cenderung menurun seiring bertambahnya usia. Namun, pola penurunan tersebut tidak berbentuk garis lurus melainkan melengkung berarti mengindikasikan hubungan non-linear. Pada individu yang memiliki penyakit jantung, penurunan thalach tampak lebih tajam di usia muda dan mulai melandai seiring bertambahnya usia. Sebaliknya, pada individu yang tidak memiliki penyakit jantung, meskipun penurunan juga terjadi, terdapat sedikit kenaikan kembali di usia tua yang mungkin disebabkan oleh variasi data.

g). Heatmap korelasi

library(tidyverse)
## Warning: package 'tidyverse' was built under R version 4.3.3
## Warning: package 'tibble' was built under R version 4.3.3
## Warning: package 'tidyr' was built under R version 4.3.3
## Warning: package 'readr' was built under R version 4.3.3
## Warning: package 'purrr' was built under R version 4.3.3
## Warning: package 'stringr' was built under R version 4.3.3
## Warning: package 'forcats' was built under R version 4.3.3
## Warning: package 'lubridate' was built under R version 4.3.3
## ── Attaching core tidyverse packages ──────────────────────── tidyverse 2.0.0 ──
## ✔ forcats   1.0.0     ✔ stringr   1.5.1
## ✔ lubridate 1.9.4     ✔ tibble    3.2.1
## ✔ purrr     1.0.2     ✔ tidyr     1.3.1
## ✔ readr     2.1.5     
## ── Conflicts ────────────────────────────────────────── tidyverse_conflicts() ──
## ✖ dplyr::filter() masks stats::filter()
## ✖ dplyr::lag()    masks stats::lag()
## ℹ Use the conflicted package (<http://conflicted.r-lib.org/>) to force all conflicts to become errors
library(ggcorrplot)
## Warning: package 'ggcorrplot' was built under R version 4.3.3
# Ambil hanya kolom numerik
data_num <- data %>% select(where(is.numeric))

# Hitung korelasi
cor_matrix <- cor(data_num, use = "complete.obs")

# Plot heatmap korelasi
ggcorrplot(cor_matrix,
           method = "square",
           type = "lower",
           lab = TRUE,
           title = "Heatmap Korelasi Variabel Numerik")

Intensitas warna mencerminkan kekuatan korelasi, warna yang lebih gelap menunjukkan hubungan yang lebih kuat. Terlihat adanya korelasi positif yang cukup kuat antara oldpeak dengan exang sebesar 0.26 dan korelasi negatif yang cukup kuat antara oldpeak dengan thalach sebesar -0.38 dan antara exang dengan thalach sebesar -0.34. Sebaliknya, beberapa variabel seperti fbs dan restecg tampak memiliki korelasi yang lemah dengan variabel lainnya karena warna pada sel-sel yang bersesuaian cenderung mendekati putih atau memiliki nilai korelasi yang kecil.

h). Summary hasil analisis

Hal-hal yang dapat mencegah penyakit jantung adalah menerapkan gaya hidup sehat seperti menjaga pola makan rendah lemak dan kolesterol, rutin berolahraga, menghindari rokok dan alkohol, mengelola stres, serta melakukan pemeriksaan kesehatan secara berkala untuk memantau tekanan darah, kadar gula, dan kolesterol. Sedangkan, aspek yang merupakan indikasi penyakit jantung adalah usia lanjut, jenis kelamin laki-laki, tekanan darah tinggi (trestbps), kadar kolesterol tinggi (chol), nyeri dada tipe tertentu (cp), penurunan segmen ST akibat stres (oldpeak), denyut jantung maksimum rendah (thalach), adanya angina saat beraktivitas (exang), jumlah pembuluh darah yang bermasalah (ca) dan kondisi thalassemia yang tidak normal (thal).

2. Diagram dari data yang sama dengan contoh

# Buat data
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,1300,1250,1300,1250,
                    1200,1150,1100,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", 5),
               rep("UMKM", 7))
)

# Plot pakai ggplot
ggplot(data, aes(x = Konsumsi_GWh, y = Biaya_per_kWh, color = Konsumen)) +
  geom_point(size = 3) +
  geom_smooth(method = "lm", se = FALSE) +
  labs(title = "Biaya Listrik per kWh Menurun Seiring Kenaikan Konsumsi",
       x = "Total Konsumsi Listrik (GWh)",
       y = "Biaya per kWh (Rp/kWh)",
       color = "Konsumen") +
  theme_minimal()
## `geom_smooth()` using formula = 'y ~ x'

Grafik tersebut menggambarkan hubungan terbalik antara total konsumsi listrik dengan biaya listrik per kWh untuk berbagai kategori konsumen, yaitu Industri, Kantor Pemerintah, Rumah Tangga, dan UMKM. Terlihat bahwa semakin tinggi total konsumsi listrik, maka biaya listrik semakin menurun untuk semua kategori konsumen. Namun, tingkat penurunan biaya per kWh terhadap peningkatan konsumsi berbeda-beda antar kategori konsumen, yang ditunjukkan oleh kemiringan garis tren yang berbeda, artinya terdapat struktur tarif yang berbeda berdasarkan volume konsumsi dan jenis konsumen.