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