library(tidyverse)
## Warning: package 'tidyverse' was built under R version 4.3.3
## Warning: package 'ggplot2' 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 'dplyr' 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 ──
## ✔ dplyr 1.1.4 ✔ readr 2.1.5
## ✔ forcats 1.0.0 ✔ stringr 1.5.1
## ✔ ggplot2 3.5.1 ✔ tibble 3.2.1
## ✔ lubridate 1.9.3 ✔ tidyr 1.3.1
## ✔ purrr 1.0.2
## ── 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(summarytools)
## Warning: package 'summarytools' was built under R version 4.3.3
##
## Attaching package: 'summarytools'
##
## The following object is masked from 'package:tibble':
##
## view
library(ggplot2)
library(dplyr)
library(ggcorrplot)
## Warning: package 'ggcorrplot' was built under R version 4.3.3
# Baca dataset
data <- read.csv("C:/Users/ASUS/Documents/SEMESTER 4/PSD/heart.csv")
# 1. Pengecekan Missing Values
# Cek missing values
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
Penanganan:
Jika terdapat nilai yang hilang, bisa dihapus atau diisi. Dalam kasus
ini, kita hapus jika hanya sedikit.
# Hapus baris dengan NA
data <- na.omit(data)
# 2. Pengecekan Duplikat
# Jumlah data duplikat
sum(duplicated(data))
## [1] 1
# Hapus duplikat
data <- data[!duplicated(data), ]
Penanganan:
Duplikat dihapus karena berpotensi mendistorsi analisis.
# 3. Pengecekan Outlier
# Ambil variabel numerik
numeric_data <- select_if(data, is.numeric)
# Boxplot semua variabel numerik
boxplot(numeric_data, main = "Boxplot Variabel Numerik", las = 2)
Penanganan Outlier:
Outlier akan dihapus menggunakan metode IQR.
remove_outliers <- function(x) {
Q1 <- quantile(x, 0.25, na.rm = TRUE)
Q3 <- quantile(x, 0.75, na.rm = TRUE)
IQR <- Q3 - Q1
x[x < (Q1 - 1.5 * IQR) | x > (Q3 + 1.5 * IQR)] <- NA
return(x)
}
# Terapkan fungsi ke semua variabel numerik
clean_data <- numeric_data %>% mutate_all(remove_outliers)
# Hapus NA akibat outlier
clean_data <- na.omit(clean_data)
# 4. Statistik Deskriptif
# Statistik deskriptif setelah pembersihan
dfSummary(clean_data)
## Data Frame Summary
## clean_data
## Dimensions: 228 x 14
## Duplicates: 0
##
## -------------------------------------------------------------------------------------------------------------
## No Variable Stats / Values Freqs (% of Valid) Graph Valid Missing
## ---- ----------- -------------------------- --------------------- ---------------------- ---------- ---------
## 1 age Mean (sd) : 53.3 (9.2) 40 distinct values : 228 0
## [integer] min < med < max: . . . : : . (100.0%) (0.0%)
## 29 < 54 < 76 : : : : : :
## IQR (CV) : 15 (0.2) : : : : : : .
## : : : : : : : :
##
## 2 sex Min : 0 0 : 74 (32.5%) IIIIII 228 0
## [integer] Mean : 0.7 1 : 154 (67.5%) IIIIIIIIIIIII (100.0%) (0.0%)
## Max : 1
##
## 3 cp Mean (sd) : 0.9 (1) 0 : 108 (47.4%) IIIIIIIII 228 0
## [integer] min < med < max: 1 : 42 (18.4%) III (100.0%) (0.0%)
## 0 < 1 < 3 2 : 61 (26.8%) IIIII
## IQR (CV) : 2 (1.1) 3 : 17 ( 7.5%) I
##
## 4 trestbps Mean (sd) : 128.7 (15.3) 38 distinct values . : . 228 0
## [integer] min < med < max: : : : (100.0%) (0.0%)
## 94 < 130 < 170 : : : :
## IQR (CV) : 20 (0.1) : : : : : .
## . : : : : : : .
##
## 5 chol Mean (sd) : 242.4 (44.3) 132 distinct values . : 228 0
## [integer] min < med < max: : : : (100.0%) (0.0%)
## 131 < 239 < 360 . : : : :
## IQR (CV) : 59.5 (0.2) : : : : : :
## . : : : : : : : : .
##
## 6 fbs 1 distinct value 0 : 228 (100.0%) IIIIIIIIIIIIIIIIIIII 228 0
## [integer] (100.0%) (0.0%)
##
## 7 restecg Mean (sd) : 0.5 (0.5) 0 : 105 (46.1%) IIIIIIIII 228 0
## [integer] min < med < max: 1 : 121 (53.1%) IIIIIIIIII (100.0%) (0.0%)
## 0 < 1 < 2 2 : 2 ( 0.9%)
## IQR (CV) : 1 (0.9)
##
## 8 thalach Mean (sd) : 151.1 (22.5) 81 distinct values : : 228 0
## [integer] min < med < max: . : : (100.0%) (0.0%)
## 88 < 155 < 202 : : : : :
## IQR (CV) : 30.8 (0.1) : : : : : : .
## . . : : : : : : :
##
## 9 exang Min : 0 0 : 156 (68.4%) IIIIIIIIIIIII 228 0
## [integer] Mean : 0.3 1 : 72 (31.6%) IIIIII (100.0%) (0.0%)
## Max : 1
##
## 10 oldpeak Mean (sd) : 0.9 (1) 35 distinct values : 228 0
## [numeric] min < med < max: : (100.0%) (0.0%)
## 0 < 0.6 < 4 :
## IQR (CV) : 1.6 (1.1) : . .
## : : : : . : .
##
## 11 slope Mean (sd) : 1.5 (0.6) 0 : 11 ( 4.8%) 228 0
## [integer] min < med < max: 1 : 103 (45.2%) IIIIIIIII (100.0%) (0.0%)
## 0 < 1.5 < 2 2 : 114 (50.0%) IIIIIIIIII
## IQR (CV) : 1 (0.4)
##
## 12 ca Mean (sd) : 0.5 (0.7) 0 : 146 (64.0%) IIIIIIIIIIII 228 0
## [integer] min < med < max: 1 : 55 (24.1%) IIII (100.0%) (0.0%)
## 0 < 0 < 2 2 : 27 (11.8%) II
## IQR (CV) : 1 (1.5)
##
## 13 thal Mean (sd) : 2.3 (0.6) 1 : 11 ( 4.8%) 228 0
## [integer] min < med < max: 2 : 134 (58.8%) IIIIIIIIIII (100.0%) (0.0%)
## 1 < 2 < 3 3 : 83 (36.4%) IIIIIII
## IQR (CV) : 1 (0.2)
##
## 14 target Min : 0 0 : 96 (42.1%) IIIIIIII 228 0
## [integer] Mean : 0.6 1 : 132 (57.9%) IIIIIIIIIII (100.0%) (0.0%)
## Max : 1
## -------------------------------------------------------------------------------------------------------------
# Buat tabulasi silang
table_fbs <- table(data$fbs, data$target)
table_fbs
##
## 0 1
## 0 116 141
## 1 22 23
# Hitung proporsi
prop.table(table_fbs, margin = 1) * 100
##
## 0 1
## 0 45.13619 54.86381
## 1 48.88889 51.11111
Orang dengan kadar gula darah > 120 (fbs = 1) tidak secara signifikan lebih banyak mengalami penyakit jantung, karena proporsinya (51.11%) lebih rendah sedikit dibanding yang fbs = 0 (54.86%).Jadi, kadar gula darah > 120 bukan faktor dominan dalam penyakit jantung.
# Hitung frekuensi jenis nyeri dada pada pasien dengan penyakit jantung (target = 1)
nyeri_terbanyak <- data %>%
filter(target == 1) %>%
count(cp, sort = TRUE)
nyeri_terbanyak
## cp n
## 1 2 68
## 2 1 41
## 3 0 39
## 4 3 16
# Visualisasikan jumlah jenis nyeri dada
ggplot(nyeri_terbanyak, aes(x = factor(cp), y = n, fill = factor(cp))) +
geom_bar(stat = "identity") +
labs(
title = "Distribusi Jenis Nyeri Dada pada Pasien Penyakit Jantung",
x = "Jenis Nyeri Dada (cp)",
y = "Jumlah Pasien",
fill = "Kode CP"
) +
theme_minimal(base_size = 14)
Grafik menunjukkan distribusi jenis nyeri dada (kode CP) pada pasien penyakit jantung. Jenis nyeri dada dengan kode 2 merupakan yang paling banyak dialami pasien, diikuti oleh kode 1 dan 0 dengan jumlah yang hampir sama. Jenis nyeri dada dengan kode 3 memiliki jumlah pasien paling sedikit. Ini menunjukkan bahwa jenis nyeri dada tertentu khususnya kode 2 lebih umum dikaitkan dengan penyakit jantung.
# Fungsi bantu untuk membuat bar plot proporsi
plot_prop <- function(var_name) {
data %>%
group_by(.data[[var_name]], target) %>%
summarise(n = n(), .groups = 'drop') %>%
group_by(.data[[var_name]]) %>%
mutate(prop = n / sum(n)) %>%
ggplot(aes(x = factor(.data[[var_name]]), y = prop, fill = factor(target))) +
geom_bar(stat = "identity", position = "dodge") +
labs(title = paste("Proporsi Heart Disease berdasarkan", var_name),
x = var_name,
y = "Proporsi",
fill = "Heart Disease (1 = Ya)") +
theme_minimal()
}
plot_prop("cp")
Grafik menunjukkan proporsi penyakit jantung berdasarkan nilai cp. Bar berwarna orange mewakili proporsi individu tanpa penyakit jantung (0), dan bar berwarna biru muda mewakili individu dengan penyakit jantung (1). Dari grafik, terlihat bahwa pada kategori cp = 0 (tanpa nyeri dada atau nyeri dada atipikal), proporsi individu yang mengalami penyakit jantung lebih rendah dibandingkan dengan kategori cp = 2 atau cp = 3, di mana proporsi individu dengan penyakit jantung lebih tinggi.
plot_prop("ca")
Grafik menunjukkan proporsi penyakit jantung berdasarkan nilai ca. Bar berwarna orange mewakili individu tanpa penyakit jantung (0), sedangkan bar biru muda mewakili individu dengan penyakit jantung (1). Terlihat bahwa semakin tinggi nilai ca, proporsi individu tanpa penyakit jantung cenderung meningkat, sedangkan proporsi penyakit jantung lebih banyak ditemukan pada ca bernilai 0 atau 4.
plot_prop("thal")
Grafik menunjukkan proporsi individu dengan dan tanpa penyakit jantung berdasarkan nilai thal. Warna orange mewakili individu tanpa penyakit jantung, sedangkan warna biru muda mewakili individu yang mengidap penyakit jantung. Terlihat bahwa pada nilai thal = 2, proporsi individu dengan penyakit jantung cukup dominan, menunjukkan hubungan yang kuat antara nilai ini dan kemungkinan menderita penyakit jantung. Sebaliknya, pada nilai thal = 3, mayoritas individu justru tidak menderita penyakit jantung. Sementara itu, pada nilai thal = 1, proporsi individu tanpa penyakit jantung juga lebih tinggi dibandingkan yang menderita dan pada thal= 0, proporsi individu dengan dan tanpa penyakit jantung sama.
# Scatter plot dengan garis regresi linear
ggplot(data, aes(x = age, y = thalach, color = factor(target))) +
geom_point(alpha = 0.6) +
geom_smooth(method = "lm", se = FALSE) +
labs(title = "Hubungan antara Age dan Thalach",
subtitle = "Dibedakan berdasarkan status Heart Disease",
x = "Usia",
y = "Detak Jantung Maksimum (thalach)",
color = "Heart Disease") +
theme_minimal()
## `geom_smooth()` using formula = 'y ~ x'
Grafik menunjukkan hubungan antara usia dan detak jantung maksimum (thalach) yang dibedakan berdasarkan status penyakit jantung. Terlihat bahwa secara umum, detak jantung maksimum cenderung menurun seiring bertambahnya usia. Penderita penyakit jantung warna biru cenderung memiliki detak jantung maksimum yang lebih tinggi dibandingkan yang tidak menderita warna merah, terutama pada usia lebih muda.
# Ambil variabel numerik
numeric_data <- select(data, where(is.numeric))
# Hitung korelasi Pearson
cor_matrix <- cor(numeric_data)
# Visualisasi heatmap korelasi
ggcorrplot(cor_matrix,
method = "circle",
type = "lower",
lab = TRUE,
colors = c("red", "white", "blue"),
title = "Heatmap Korelasi Antar Variabel Numerik")
Berdasarkan heatmap korelasi antar variabel numerik, terlihat bahwa beberapa variabel memiliki hubungan yang cukup kuat terhadap variabel target (penyakit jantung). Variabel cp (jenis nyeri dada) menunjukkan korelasi positif dengan target, yang berarti semakin tinggi nilai cp, semakin besar kemungkinan seseorang menderita penyakit jantung. Selain itu, thalach (detak jantung maksimum) juga berkorelasi positif, menunjukkan bahwa individu dengan detak jantung maksimum yang lebih tinggi cenderung memiliki risiko lebih besar. Sebaliknya, variabel oldpeak (depresi ST setelah olahraga) dan exang (angina akibat olahraga) menunjukkan korelasi negatif dengan target, artinya semakin tinggi nilainya, semakin kecil kemungkinan individu tersebut mengidap penyakit jantung. Sementara itu, variabel lain seperti chol, fbs, dan restecg memiliki korelasi yang sangat lemah terhadap target, menunjukkan bahwa pengaruhnya terhadap risiko penyakit jantung relatif kecil.
SUMMARY HASIL Dari hasil eksplorasi data heat disease, ditemukan beberapa pola penting mengenai faktor dan indikasi penyakit jantung
Pada pengecekkan missing value dan duplikasi data tidak mengandung nilai hilang maupun duplikat, sehingga dapat dianalisis tanpa pembersihan besar.
Pada pengecekkan outlier menggunakan boxplot ditemukan beberapa outlier pada variabel numerik seperti thalach dan oldpeak, namun jika dilihat secara medis masih mungkin terjadi dan tidak langsung dihapus.
Pada analisis distribusi penyakit jantung, individu dengan kadar gula darah putih > 120 mg/dl lebih sering memilikipenyakit jantung. Usia yang paling banyak terkena penyakit jantung adalah usia antara 50 hingga 60 tahun.
Pada analisis gula darah, individu dengan kadar gula darah putih > 120 mg/dl lebih sering memiliki penyakit jantung, mewskipun tidak dominan secara keseluruhan.
Pada hubungan variabel numerik ditemukan korelasi negatif antara variabel ege dan thalach, menunjukkan bahwa detak jantung maksimum menurun seiring bertambahnya usia seseorang.
Variabel kategorikal Jenis nyeri dada (cp) dan tipe thalassemia (thal) menunjukkan proporsi signifikan terhadap penyakit jantung.
Jumlah pembulu besar yang terdeteksi oleh fluoroskopi (ca) juga menjadi indikator kuat.
Pencegahan Penyakit Jantung - Menjaga tekanan darah dan gula darah tetap normal, karena tekanan darah tinggi dan kadar gula darah tinggi berkorelasi dengan resiko jantung.
Aktivitas fisik teratur, karena detak jantung maksimum yang tinggi (dengan kondisi jantung sehat) mengindikasikan kebugaran yang lebih baik.
Pola makan sehat, dengan mengurangi makanan tinggi lemak jenuh, gula, dan garam.
Tidak merokok.
Pemeriksaan rutin, terutama untuk individu dengan usia di atas 45 tahun.
##2. Dari data berikut, Buat diagram yang sama dengan contoh. Serta jelaskan maksud setiap dari bagian sintag tersebut. Kemudian interpretasikan.
#Membuat data frame
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 Pemerintahan", 5),
rep("UMKM", 7))
)
ggplot(data, aes(x = Konsumsi_GWh, y = Biaya_per_kWh, color = Konsumen)) +
geom_point(size = 2) +
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 (Rupiah)",
caption = "Data simulasi untuk keperluan edukasi") +
coord_cartesian(xlim = c(0, 420)) +
theme_minimal(base_size = 16)
## `geom_smooth()` using formula = 'y ~ x'
Interpretasi
Grafik menunjukkan bahwa biaya listrik per kWh cenderung menurun seiring dengan meningkatnya total konsumsi listrik. Pola ini terlihat pada seluruh kategori konsumen, penurunan paling signifikan berada pada sektor industri. Hal ini menunjukkan bahwa semakin besar konsumsi listrik, semakin rendah tarif per kWh yang dikenakan, mencerminkan andanya efesiensi atau skema tarif berjenjang. Sebaliknya, UMKM dan rumah tangga memiliki konsumsi lebih rendah tetap dikenakan tarif per kWh yang relatif tinggi.