Project ini bertujuan untuk mengimplementasikan metode klasifikasi statistika pada data meteorologi sintetis. Dataset ini mencakup berbagai parameter cuaca yang digunakan untuk memprediksi empat kategori utama: Rainy, Sunny, Cloudy, dan Snowy.

Variabel yang digunakan meliputi:


Analisis ini dilakukan pada data yang telah melalui tahap preprocessing untuk memastikan model LDA dan Multinomial bekerja secara optimal.

Load Library

options(repos = c(CRAN = "[https://cloud.r-project.org](https://cloud.r-project.org)"))

library(nnet)      
library(MASS)        
library(caret)
library(ggplot2)
library(biotools)

Tahap awal pada penelitian dimulai dengan memuat paket yang diperlukan. Setiap paket memiliki fungsi spesifik sebagai berikut: - MASS → membangun model Linear Discriminant Analysis (LDA) - nnet → membangun model Multinomial Logistic Regression - caret → evaluasi performa model (confusion matrix, accuracy, Kappa) - ggplot2 → visualisasi data - biotools → uji asumsi LDA (Box’s M test) Tujuan tahap ini dilakukan adalah untuk menyiapkan lingkungan analisis agar seluruh metode dapat dijalankan dengan optimal.

Load Data

weather_df <- read.csv("weather_classification_data.csv")

Dataset yang digunakan berisi informasi kondisi cuaca berdasarkan beberapa variabel meteorologi seperti suhu, kelembapan, tekanan udara, dan lain-lain. Variabel targetnya adalah Weather_Type yang menunjukkan jenis cuaca.

Struktur dan Cek Missing Value

# Struktur data
str(weather_df)
## 'data.frame':    13200 obs. of  11 variables:
##  $ Temperature         : num  14 39 30 38 27 32 -2 3 3 28 ...
##  $ Humidity            : int  73 96 64 83 74 55 97 85 83 74 ...
##  $ Wind.Speed          : num  9.5 8.5 7 1.5 17 3.5 8 6 6 8.5 ...
##  $ Precipitation....   : num  82 71 16 82 66 26 86 96 66 107 ...
##  $ Cloud.Cover         : chr  "partly cloudy" "partly cloudy" "clear" "clear" ...
##  $ Atmospheric.Pressure: num  1011 1011 1019 1026 991 ...
##  $ UV.Index            : int  2 7 5 7 1 2 1 1 0 8 ...
##  $ Season              : chr  "Winter" "Spring" "Spring" "Spring" ...
##  $ Visibility..km.     : num  3.5 10 5.5 1 2.5 5 4 3.5 1 7.5 ...
##  $ Location            : chr  "inland" "inland" "mountain" "coastal" ...
##  $ Weather.Type        : chr  "Rainy" "Cloudy" "Sunny" "Sunny" ...
# Cek missing value
colSums(is.na(weather_df))
##          Temperature             Humidity           Wind.Speed 
##                    0                    0                    0 
##    Precipitation....          Cloud.Cover Atmospheric.Pressure 
##                    0                    0                    0 
##             UV.Index               Season      Visibility..km. 
##                    0                    0                    0 
##             Location         Weather.Type 
##                    0                    0

Melakukan pemeriksaan nilai kosong untuk memastikan kualitas data. Hasil yang di peroleh menunjukkan tidak terdapat missing value, sehingga data siap digunakan tanpa imputasi.

Data Cleaning & Sampling

# 1. Membersihkan Nama Kolom
colnames(weather_df) <- c("Temperature", "Humidity", "Wind_Speed", "Precipitation", 
                          "Cloud_Cover", "Pressure", "UV_Index", "Season", 
                          "Visibility", "Location", "Weather_Type")

# 2. Mengambil Sampel Acak 500 Data
set.seed(123) # Agar hasil random selalu sama setiap kali di-run
weather_df <- weather_df[sample(nrow(weather_df), 500), ]

Pada tahap ini dilakukan persiapan data sebelum analisis. Pertama, nama kolom diperbaiki agar lebih jelas dan mudah dipahami. Selanjutnya diambil sampel acak sebanyak 500 data menggunakan set.seed(123) supaya hasil sampling konsisten setiap kali dijalankan serta untuk mempercepat proses komputasi.

Mengubah Variabel Kategori menjadi Faktor

weather_df$Season <- as.factor(weather_df$Season)
weather_df$Cloud_Cover <- as.factor(weather_df$Cloud_Cover)
weather_df$Location <- as.factor(weather_df$Location)
weather_df$Weather_Type <- as.factor(weather_df$Weather_Type)

Variabel kategorik yaitu Season, Cloud_Cover, Location, dan Weather_Type diubah menjadi tipe faktor agar dapat digunakan dengan benar pada metode klasifikasi seperti LDA dan regresi logistik multinomial.

Cek Distribusi Target

# Struktur data setelah sampling
str(weather_df)
## 'data.frame':    500 obs. of  11 variables:
##  $ Temperature  : num  41 27 -7 4 22 42 -7 31 44 1 ...
##  $ Humidity     : int  76 74 82 91 38 33 77 33 35 94 ...
##  $ Wind_Speed   : num  10.5 0.5 11.5 6.5 2.5 5.5 1.5 8 7 14.5 ...
##  $ Precipitation: num  76 19 95 68 11 19 66 14 2 95 ...
##  $ Cloud_Cover  : Factor w/ 4 levels "clear","cloudy",..: 3 4 3 4 1 4 3 1 4 3 ...
##  $ Pressure     : num  1134 1018 998 989 1025 ...
##  $ UV_Index     : int  7 3 1 1 11 8 0 10 6 1 ...
##  $ Season       : Factor w/ 4 levels "Autumn","Spring",..: 2 1 4 4 2 2 4 2 2 4 ...
##  $ Visibility   : num  3.5 8.5 3.5 4.5 7.5 6.5 4 5.5 6.5 2 ...
##  $ Location     : Factor w/ 3 levels "coastal","inland",..: 2 1 2 3 3 2 2 2 2 2 ...
##  $ Weather_Type : Factor w/ 4 levels "Cloudy","Rainy",..: 1 1 3 3 4 4 3 4 4 3 ...
# Distribusi kelas setelah sampling
table(weather_df$Weather_Type)
## 
## Cloudy  Rainy  Snowy  Sunny 
##    124    127    115    134
# Visualisasi Distribusi
ggplot(weather_df, aes(Weather_Type, fill = Weather_Type)) +
  geom_bar() +
  theme_minimal() +
  labs(title="Distribusi Kelas Cuaca (Sampel 500 Data)")

Tahap ini bertujuan untuk memahami kondisi data setelah proses sampling. Fungsi str() digunakan untuk melihat struktur dataset seperti tipe variabel dan jumlah observasi, sehingga dapat dipastikan data sudah sesuai untuk analisis. Selanjutnya, table() digunakan untuk melihat jumlah masing-masing kelas dan Weather_Type guna untuk mengetahui apakah distribusi kelas seimbang atau tidak. Terakhir, grafik bar dibuat menggunakan ggplot untuk memvisualisasikan distribusi kelas secara lebih jelas sehingga memudahkan melihat kelas cuaca mana yang paling dominan pada sampel 500 data.

Ringkasan statistik untuk melihat outlier

summary(weather_df[, c("Temperature", "Humidity", "Wind_Speed", "Precipitation", "Pressure")])
##   Temperature       Humidity        Wind_Speed     Precipitation  
##  Min.   :-24.0   Min.   : 20.00   Min.   : 0.000   Min.   :  0.0  
##  1st Qu.: 11.0   1st Qu.: 56.00   1st Qu.: 4.500   1st Qu.: 18.0  
##  Median : 22.0   Median : 70.00   Median : 8.000   Median : 56.0  
##  Mean   : 20.2   Mean   : 68.55   Mean   : 9.021   Mean   : 52.1  
##  3rd Qu.: 31.0   3rd Qu.: 84.00   3rd Qu.:12.625   3rd Qu.: 82.0  
##  Max.   :109.0   Max.   :109.00   Max.   :41.000   Max.   :109.0  
##     Pressure     
##  Min.   : 812.0  
##  1st Qu.: 996.1  
##  Median :1009.5  
##  Mean   :1007.6  
##  3rd Qu.:1016.0  
##  Max.   :1187.7
cor_matrix <- cor(weather_df[, sapply(weather_df, is.numeric)])
print(cor_matrix)
##                Temperature   Humidity   Wind_Speed Precipitation    Pressure
## Temperature    1.000000000 -0.2746240  0.005518159    -0.3407581  0.18948408
## Humidity      -0.274623992  1.0000000  0.326115333     0.6260091 -0.11775474
## Wind_Speed     0.005518159  0.3261153  1.000000000     0.4194789 -0.05203059
## Precipitation -0.340758081  0.6260091  0.419478933     1.0000000 -0.20211785
## Pressure       0.189484084 -0.1177547 -0.052030589    -0.2021178  1.00000000
## UV_Index       0.443556835 -0.4668041 -0.112364365    -0.3477374  0.12971614
## Visibility     0.274792463 -0.4864713 -0.260099057    -0.5016860  0.20680900
##                 UV_Index Visibility
## Temperature    0.4435568  0.2747925
## Humidity      -0.4668041 -0.4864713
## Wind_Speed    -0.1123644 -0.2600991
## Precipitation -0.3477374 -0.5016860
## Pressure       0.1297161  0.2068090
## UV_Index       1.0000000  0.4053526
## Visibility     0.4053526  1.0000000

Langkah ini digunakan untuk memahami karakteristik variabel numerik sebelum pemodelan. Fungsi summary() digunakan untuk melihat statistik deskriptif seperti nilai minimum, maksimum, mean, dan median pada variabel Temperature, Humidity, Wind_Speed, Precipitation, dan Pressure. Tujuannya untuk mengetahui gambaran umum data serta mendeteksi kemungkinan adanya nilai ekstrem atau outlier.

Selanjutnya dibuat matriks korelasi menggunakan cor() untuk melihat hubungan antar variabel numerik. Dari hasil korelasi ini dapat diketahui apakah terdapat hubungan yang sangat kuat antar variabel (multikolinearitas). Informasi ini penting karena korelasi yang terlalu tinggi bisa mempengaruhi performa model klasifikasi yang akan dibangun.

Boxplot untuk menampilkan Kelembapan per Tipe Cuaca

ggplot(weather_df, aes(x = Weather_Type, y = Humidity, fill = Weather_Type)) +
  geom_boxplot() +
  theme_minimal() +
  labs(title = "Variasi Kelembapan pada Setiap Tipe Cuaca",
       x = "Weather Type", y = "Humidity (Standardized)")

Visualisasi boxplot ini digunakan untuk melihat perbedaan distribusi kelembapan (Humidity) pada setiap kategori Weather_Type. Melalui grafik ini, kita bisa membandingkan nilai median, sebaran data, serta mendeteksi outlier pada masing-masing tipe cuaca. Jika terlihat perbedaan pola antar kelas (misalnya ada tipe cuaca yang memiliki kelembapan lebih tinggi atau lebih rendah), maka variabel Humidity berpotensi menjadi variabel yang penting dalam proses klasifikasi cuaca menggunakan model LDA maupun regresi multinomial.

Visualisasi Sebaran untuk Analisis Diskriminan

ggplot(weather_df, aes(x = Temperature, y = Humidity, color = Weather_Type)) +
  geom_point(alpha = 0.7, size = 2) +
  theme_minimal() +
  labs(title = "Scatter Plot: Temperature vs Humidity (Sampel 500)",
       subtitle = "Pola sebaran antar kelas lebih mudah dibedakan")

Scatter plot digunakan untuk melihat hubungan antara Temperature dan Humidity pada tiap kategori Weather_Type. Melalui grafik ini kita bisa mengamati apakah terdapat pola pemisahan antar kelas cuaca berdasarkan kombinasi suhu dan kelembapan. Jika titik-titik dari tiap tipe cuaca membentuk kelompok (cluster) yang cukup berbeda, maka kedua variabel ini memiliki kemampuan yang baik untuk membedakan jenis cuaca dan cocok digunakan sebagai variabel prediktor pada model klasifikasi.

Density Plot untuk menampilkan Distribusi Suhu per Tipe Cuaca

ggplot(weather_df, aes(x = Temperature, fill = Weather_Type)) +
  geom_density(alpha = 0.5) +
  theme_minimal() +
  labs(title = "Distribusi Suhu berdasarkan Tipe Cuaca",
       x = "Temperature (Standardized)", y = "Density")

Plot density digunakan untuk melihat sebaran nilai Temperature pada setiap kategori Weather_Type. Dari grafik ini kita bisa membandingkan apakah distribusi suhu tiap tipe cuaca saling tumpang tindih atau memiliki pola yang berbeda. Jika kurva density antar kelas terlihat berbeda atau bergeser, artinya suhu merupakan variabel yang cukup informatif untuk membedakan tipe cuaca. Sebaliknya, jika kurvanya sangat bertumpuk, maka kemampuan suhu dalam membedakan kelas cuaca relatif rendah.

Cek Korelasi antar variabel numerik

cor_matrix <- cor(weather_df[, sapply(weather_df, is.numeric)])
print(cor_matrix)
##                Temperature   Humidity   Wind_Speed Precipitation    Pressure
## Temperature    1.000000000 -0.2746240  0.005518159    -0.3407581  0.18948408
## Humidity      -0.274623992  1.0000000  0.326115333     0.6260091 -0.11775474
## Wind_Speed     0.005518159  0.3261153  1.000000000     0.4194789 -0.05203059
## Precipitation -0.340758081  0.6260091  0.419478933     1.0000000 -0.20211785
## Pressure       0.189484084 -0.1177547 -0.052030589    -0.2021178  1.00000000
## UV_Index       0.443556835 -0.4668041 -0.112364365    -0.3477374  0.12971614
## Visibility     0.274792463 -0.4864713 -0.260099057    -0.5016860  0.20680900
##                 UV_Index Visibility
## Temperature    0.4435568  0.2747925
## Humidity      -0.4668041 -0.4864713
## Wind_Speed    -0.1123644 -0.2600991
## Precipitation -0.3477374 -0.5016860
## Pressure       0.1297161  0.2068090
## UV_Index       1.0000000  0.4053526
## Visibility     0.4053526  1.0000000

Langkah ini berfungsi untuk menghitung dan melihat korelasi antar variabel numerik pada dataset. Matriks korelasi menunjukkan seberapa kuat hubungan antar variabel dalam rentang -1 sampai 1. - Nilai mendekati 1 → hubungan positif kuat (jika satu naik, yang lain ikut naik). - Nilai mendekati -1 → hubungan negatif kuat (jika satu naik, yang lain turun). - Nilai mendekati 0 → hubungan lemah/tidak ada hubungan linear.

Tujuan utama pengecekan ini adalah untuk mendeteksi multikolinearitas. Jika ada variabel yang memiliki korelasi sangat tinggi (biasanya > 0.8), maka variabel tersebut bisa memberikan informasi yang mirip dan dapat mempengaruhi performa model klasifikasi seperti LDA dan regresi multinomial.

Train Test Split

set.seed(123)
train_index <- createDataPartition(weather_df$Weather_Type, 
                                   p = 0.8, 
                                   list = FALSE)

train_data <- weather_df[train_index, ]
test_data  <- weather_df[-train_index, ]

# Cek Jumlah Data
dim(train_data)
## [1] 402  11
dim(test_data)
## [1] 98 11

Langkah ini merupakan proses membagi dataset menjadi data training dan data testing. Pertama, set.seed(123) digunakan agar proses pembagian data bersifat reproducible, sehingga hasil pembagian akan selalu sama setiap kali kode dijalankan. Fungsi createDataPartition() digunakan untuk membagi data secara stratified, artinya proporsi tiap kelas pada variabel target Weather_Type tetap seimbang pada data training dan testing. Dataset dibagi dengan komposisi 80% data training dan 20% data testing. train_data digunakan untuk melatih model (LDA dan Multinomial) dan test_data digunakan untuk menguji performa model pada data yang belum pernah dilihat sebelumnya. Terakhir, fungsi dim() digunakan untuk memastikan jumlah baris dan kolom pada masing-masing dataset setelah proses pembagian.

Standarisasi Variabel Numerik

num_cols <- c("Temperature","Humidity","Wind_Speed",
              "Precipitation","Pressure","UV_Index","Visibility")

preproc <- preProcess(train_data[,num_cols], method = c("center","scale"))

train_data[,num_cols] <- predict(preproc, train_data[,num_cols])
test_data[,num_cols]  <- predict(preproc, test_data[,num_cols])

Langkah ini merupakan proses standarisasi (scaling) variabel numerik sebelum pemodelan dilakukan. Pertama, membuat daftar variabel numerik yang akan distandarisasi, yaitu suhu, kelembapan, kecepatan angin, curah hujan, tekanan udara, UV index, dan jarak pandang. Fungsi preProcess() dengan metode center dan scale digunakan untuk: - Center: mengubah rata-rata tiap variabel menjadi 0 - Scale: mengubah standar deviasi menjadi 1 Tujuan standarisasi ini dilakukan adalah agar semua variabel memiliki skala yang sama, sehingga tidak ada variabel yang mendominasi model hanya karena memiliki nilai yang lebih besar. Langkah ini penting karena Model LDA dan regresi logistik sensitif terhadap perbedaan skala. Standarisasi dihitung dari data training, lalu diterapkan ke data testing menggunakan predict(). Hal ini dilakukan untuk mencegah data leakage (kebocoran informasi dari data test ke training).

Uji Homogenitas Kovarians

# Uji Asumsi LDA
boxM(train_data[,num_cols], train_data$Weather_Type)
## 
##  Box's M-test for Homogeneity of Covariance Matrices
## 
## data:  train_data[, num_cols]
## Chi-Sq (approx.) = 456.09, df = 84, p-value < 2.2e-16

Langkah ini digunakan untuk menguji asumsi utama pada metode LDA, yaitu asumsi homogenitas matriks kovarians antar kelompok kelas cuaca. Fungsi boxM() melakukan Box’s M Test, yaitu uji statistik untuk mengecek apakah varians–kovarians variabel numerik pada setiap kelas Weather_Type memiliki pola yang sama. Tujuan uji ini adalah menguji apakah LDA mengasumsikan bahwa setiap kelas memiliki matriks kovarians yang sama (homogen). Jika asumsi ini terpenuhi, maka hasil klasifikasi LDA akan lebih valid dan optimal. Cara interpretasi hasil yang dilakukan adalah Jika p-value > 0.05 → asumsi homogenitas terpenuhi → LDA layak digunakan dan Jika p-value < 0.05 → asumsi tidak terpenuhi → performa LDA bisa kurang optimal, namun model masih bisa digunakan dengan catatan interpretasi lebih hati-hati.

Analisis Diskriminan Linier (LDA)

# Membangun model LDA
model_lda <- lda(Weather_Type ~ Temperature + Humidity + Wind_Speed +
                 Precipitation + Pressure + UV_Index + Visibility,
                 data = train_data)

model_lda
## Call:
## lda(Weather_Type ~ Temperature + Humidity + Wind_Speed + Precipitation + 
##     Pressure + UV_Index + Visibility, data = train_data)
## 
## Prior probabilities of groups:
##    Cloudy     Rainy     Snowy     Sunny 
## 0.2487562 0.2537313 0.2288557 0.2686567 
## 
## Group means:
##        Temperature    Humidity   Wind_Speed Precipitation    Pressure
## Cloudy   0.2392317 -0.07499572 -0.113874808    -0.4349387  0.03771018
## Rainy    0.2061653  0.58687386  0.667264814     0.7076855 -0.09683412
## Snowy   -1.2788286  0.54128950 -0.008733432     0.7456646 -0.38257904
## Sunny    0.6731500 -0.94592773 -0.517315320    -0.9008443  0.38243828
##          UV_Index Visibility
## Cloudy -0.1512825  0.3734005
## Rainy  -0.4800781 -0.5171669
## Snowy  -0.5591187 -0.5146147
## Sunny   1.0697697  0.5810697
## 
## Coefficients of linear discriminants:
##                       LD1         LD2        LD3
## Temperature   -0.72854647 -1.19449844  0.1588008
## Humidity       0.32763868 -0.13564661 -0.4873462
## Wind_Speed     0.15459724 -0.42731629  0.0455756
## Precipitation  0.85770506 -0.10185709  0.7679367
## Pressure      -0.05591093 -0.08510031  0.1636586
## UV_Index      -0.61353965  0.80365705  0.7123481
## Visibility     0.04834540 -0.08402491 -0.7068022
## 
## Proportion of trace:
##    LD1    LD2    LD3 
## 0.7933 0.1647 0.0420
# Menampilkan hasil (Prior probabilities & Coefficients)
print(model_lda)
## Call:
## lda(Weather_Type ~ Temperature + Humidity + Wind_Speed + Precipitation + 
##     Pressure + UV_Index + Visibility, data = train_data)
## 
## Prior probabilities of groups:
##    Cloudy     Rainy     Snowy     Sunny 
## 0.2487562 0.2537313 0.2288557 0.2686567 
## 
## Group means:
##        Temperature    Humidity   Wind_Speed Precipitation    Pressure
## Cloudy   0.2392317 -0.07499572 -0.113874808    -0.4349387  0.03771018
## Rainy    0.2061653  0.58687386  0.667264814     0.7076855 -0.09683412
## Snowy   -1.2788286  0.54128950 -0.008733432     0.7456646 -0.38257904
## Sunny    0.6731500 -0.94592773 -0.517315320    -0.9008443  0.38243828
##          UV_Index Visibility
## Cloudy -0.1512825  0.3734005
## Rainy  -0.4800781 -0.5171669
## Snowy  -0.5591187 -0.5146147
## Sunny   1.0697697  0.5810697
## 
## Coefficients of linear discriminants:
##                       LD1         LD2        LD3
## Temperature   -0.72854647 -1.19449844  0.1588008
## Humidity       0.32763868 -0.13564661 -0.4873462
## Wind_Speed     0.15459724 -0.42731629  0.0455756
## Precipitation  0.85770506 -0.10185709  0.7679367
## Pressure      -0.05591093 -0.08510031  0.1636586
## UV_Index      -0.61353965  0.80365705  0.7123481
## Visibility     0.04834540 -0.08402491 -0.7068022
## 
## Proportion of trace:
##    LD1    LD2    LD3 
## 0.7933 0.1647 0.0420
# Kontribusi variabel ke fungsi diskriminan
model_lda$scaling
##                       LD1         LD2        LD3
## Temperature   -0.72854647 -1.19449844  0.1588008
## Humidity       0.32763868 -0.13564661 -0.4873462
## Wind_Speed     0.15459724 -0.42731629  0.0455756
## Precipitation  0.85770506 -0.10185709  0.7679367
## Pressure      -0.05591093 -0.08510031  0.1636586
## UV_Index      -0.61353965  0.80365705  0.7123481
## Visibility     0.04834540 -0.08402491 -0.7068022
# Prediksi ke Data Test
pred_lda <- predict(model_lda, newdata = test_data)
pred_class_lda <- pred_lda$class

cm_lda <- confusionMatrix(pred_class_lda, test_data$Weather_Type)

# Confusion Matrix 
confusionMatrix(pred_class_lda, test_data$Weather_Type)
## Confusion Matrix and Statistics
## 
##           Reference
## Prediction Cloudy Rainy Snowy Sunny
##     Cloudy     18     1     0     0
##     Rainy       4    19     0     3
##     Snowy       2     4    21     2
##     Sunny       0     1     2    21
## 
## Overall Statistics
##                                          
##                Accuracy : 0.8061         
##                  95% CI : (0.7139, 0.879)
##     No Information Rate : 0.2653         
##     P-Value [Acc > NIR] : < 2.2e-16      
##                                          
##                   Kappa : 0.7416         
##                                          
##  Mcnemar's Test P-Value : NA             
## 
## Statistics by Class:
## 
##                      Class: Cloudy Class: Rainy Class: Snowy Class: Sunny
## Sensitivity                 0.7500       0.7600       0.9130       0.8077
## Specificity                 0.9865       0.9041       0.8933       0.9583
## Pos Pred Value              0.9474       0.7308       0.7241       0.8750
## Neg Pred Value              0.9241       0.9167       0.9710       0.9324
## Prevalence                  0.2449       0.2551       0.2347       0.2653
## Detection Rate              0.1837       0.1939       0.2143       0.2143
## Detection Prevalence        0.1939       0.2653       0.2959       0.2449
## Balanced Accuracy           0.8682       0.8321       0.9032       0.8830
# Detail performa per kelas LDA
confusionMatrix(pred_class_lda, test_data$Weather_Type)$byClass
##               Sensitivity Specificity Pos Pred Value Neg Pred Value Precision
## Class: Cloudy   0.7500000   0.9864865      0.9473684      0.9240506 0.9473684
## Class: Rainy    0.7600000   0.9041096      0.7307692      0.9166667 0.7307692
## Class: Snowy    0.9130435   0.8933333      0.7241379      0.9710145 0.7241379
## Class: Sunny    0.8076923   0.9583333      0.8750000      0.9324324 0.8750000
##                  Recall        F1 Prevalence Detection Rate
## Class: Cloudy 0.7500000 0.8372093  0.2448980      0.1836735
## Class: Rainy  0.7600000 0.7450980  0.2551020      0.1938776
## Class: Snowy  0.9130435 0.8076923  0.2346939      0.2142857
## Class: Sunny  0.8076923 0.8400000  0.2653061      0.2142857
##               Detection Prevalence Balanced Accuracy
## Class: Cloudy            0.1938776         0.8682432
## Class: Rainy             0.2653061         0.8320548
## Class: Snowy             0.2959184         0.9031884
## Class: Sunny             0.2448980         0.8830128
# Hitung Akurasi LDA
acc_lda <- mean(pred_class_lda == test_data$Weather_Type)
acc_lda
## [1] 0.8061224
# Visualisasi LDA
lda_df <- data.frame(
  LD1 = pred_lda$x[,1],
  LD2 = pred_lda$x[,2],
  Weather_Type = test_data$Weather_Type
)

ggplot(lda_df, aes(LD1, LD2, color = Weather_Type)) +
  geom_point(size = 2, alpha = 0.7) +
  theme_minimal() +
  labs(title = "Visualisasi Hasil Klasifikasi LDA")

# Uji Signifikasi Model LDA
lda_pred_train <- predict(model_lda)$class

lda_table <- table(lda_pred_train, train_data$Weather_Type)
lda_table
##               
## lda_pred_train Cloudy Rainy Snowy Sunny
##         Cloudy     84     7     2     3
##         Rainy       9    91     1     9
##         Snowy       2     4    84     5
##         Sunny       5     0     5    91
# Menghitung akurasi training
train_acc_lda <- mean(lda_pred_train == train_data$Weather_Type)
train_acc_lda
## [1] 0.8706468

Model LDA dibuat untuk mengklasifikasikan tipe cuaca berdasarkan variabel numerik. Output model menunjukkan kontribusi tiap variabel dalam membedakan kelas cuaca. Model kemudian digunakan untuk memprediksi data test dan dievaluasi menggunakan confusion matrix serta akurasi. Visualisasi LD1–LD2 digunakan untuk melihat pemisahan antar kelas. Terakhir, akurasi pada data training dihitung untuk membandingkan performa training vs testing dan mengecek kemungkinan overfitting.

Heatmap Confusion Matrix (LDA)

cm_lda <- confusionMatrix(pred_class_lda, test_data$Weather_Type)
cm_lda_df <- as.data.frame(cm_lda$table)

ggplot(cm_lda_df, aes(Reference, Prediction, fill = Freq)) +
  geom_tile() +
  geom_text(aes(label = Freq), color = "white") +
  scale_fill_gradient(low = "lightblue", high = "darkblue") +
  theme_minimal() +
  labs(title = "Heatmap Confusion Matrix - LDA",
       x = "Data Aktual (Reference)",
       y = "Data Prediksi (Prediction)")

Langkah ini mengubah confusion matrix LDA menjadi bentuk data frame lalu divisualisasikan dalam bentuk heatmap. Grafik ini membantu melihat seberapa banyak prediksi yang benar dan salah pada tiap kelas cuaca. Warna yang semakin gelap menunjukkan jumlah prediksi yang semakin banyak. Jika kotak diagonal lebih gelap dibandingkan kotak lainnya, berarti model LDA mampu mengklasifikasikan tipe cuaca dengan baik.

Regresi Logistik Multinomial

# Menentukan kategori referensi
train_data$Weather_Type <- relevel(train_data$Weather_Type, ref = "Sunny")
test_data$Weather_Type  <- relevel(test_data$Weather_Type, ref = "Sunny")

# Model Multinomial (TRAIN DATA)
model_mnl <- multinom(Weather_Type ~ Temperature + Humidity + Wind_Speed + 
                      Precipitation + Pressure + UV_Index + Visibility,
                      data = train_data)
## # weights:  36 (24 variable)
## initial  value 557.290333 
## iter  10 value 260.784474
## iter  20 value 238.525385
## iter  30 value 231.091207
## final  value 231.084163 
## converged
summary(model_mnl)
## Call:
## multinom(formula = Weather_Type ~ Temperature + Humidity + Wind_Speed + 
##     Precipitation + Pressure + UV_Index + Visibility, data = train_data)
## 
## Coefficients:
##        (Intercept) Temperature  Humidity Wind_Speed Precipitation    Pressure
## Cloudy   1.5422624  -0.5192758 0.8267203  0.5154053      0.455825 -0.07468248
## Rainy    0.5470788  -0.5900514 1.1875098  1.0917122      2.143685 -0.08177496
## Snowy   -1.0097882  -4.0458478 0.9361421  0.7118054      2.323739 -0.44382368
##         UV_Index Visibility
## Cloudy -1.733200  0.5576647
## Rainy  -2.957430  0.3394850
## Snowy  -1.991782  0.5474324
## 
## Std. Errors:
##        (Intercept) Temperature  Humidity Wind_Speed Precipitation  Pressure
## Cloudy   0.3250641   0.3011106 0.2888792  0.2996359     0.2904540 0.1674409
## Rainy    0.3795414   0.3625016 0.3764416  0.3337850     0.3937612 0.2657195
## Snowy    0.5180382   0.4666539 0.4209313  0.3678436     0.4721609 0.2539332
##         UV_Index Visibility
## Cloudy 0.2727911  0.2297527
## Rainy  0.3766919  0.3030597
## Snowy  0.3495436  0.2903211
## 
## Residual Deviance: 462.1683 
## AIC: 510.1683
# Menghitung p-value koefisien multinomial
z <- summary(model_mnl)$coefficients / summary(model_mnl)$standard.errors
p_value <- (1 - pnorm(abs(z), 0, 1)) * 2
p_value
##         (Intercept) Temperature    Humidity Wind_Speed Precipitation   Pressure
## Cloudy 2.090350e-06  0.08461126 0.004212155 0.08541330  1.165655e-01 0.65558077
## Rainy  1.494659e-01  0.10358409 0.001607370 0.00107280  5.206064e-08 0.75827319
## Snowy  5.126504e-02  0.00000000 0.026149900 0.05298099  8.588364e-07 0.08049916
##            UV_Index Visibility
## Cloudy 2.103604e-10 0.01521422
## Rainy  4.218847e-15 0.26263199
## Snowy  1.210540e-08 0.05934751
# Prediksi ke TEST DATA
pred_mnl <- predict(model_mnl, newdata = test_data)
cm_mnl <- confusionMatrix(pred_mnl, test_data$Weather_Type)

# Confusion Matrix
confusionMatrix(pred_mnl, test_data$Weather_Type)
## Confusion Matrix and Statistics
## 
##           Reference
## Prediction Sunny Cloudy Rainy Snowy
##     Sunny     22      1     1     2
##     Cloudy     0     19     2     0
##     Rainy      3      2    21     0
##     Snowy      1      2     1    21
## 
## Overall Statistics
##                                           
##                Accuracy : 0.8469          
##                  95% CI : (0.7601, 0.9117)
##     No Information Rate : 0.2653          
##     P-Value [Acc > NIR] : <2e-16          
##                                           
##                   Kappa : 0.7958          
##                                           
##  Mcnemar's Test P-Value : 0.5018          
## 
## Statistics by Class:
## 
##                      Class: Sunny Class: Cloudy Class: Rainy Class: Snowy
## Sensitivity                0.8462        0.7917       0.8400       0.9130
## Specificity                0.9444        0.9730       0.9315       0.9467
## Pos Pred Value             0.8462        0.9048       0.8077       0.8400
## Neg Pred Value             0.9444        0.9351       0.9444       0.9726
## Prevalence                 0.2653        0.2449       0.2551       0.2347
## Detection Rate             0.2245        0.1939       0.2143       0.2143
## Detection Prevalence       0.2653        0.2143       0.2653       0.2551
## Balanced Accuracy          0.8953        0.8823       0.8858       0.9299
# Detail performa per kelas Multinomial
confusionMatrix(pred_mnl, test_data$Weather_Type)$byClass
##               Sensitivity Specificity Pos Pred Value Neg Pred Value Precision
## Class: Sunny    0.8461538   0.9444444      0.8461538      0.9444444 0.8461538
## Class: Cloudy   0.7916667   0.9729730      0.9047619      0.9350649 0.9047619
## Class: Rainy    0.8400000   0.9315068      0.8076923      0.9444444 0.8076923
## Class: Snowy    0.9130435   0.9466667      0.8400000      0.9726027 0.8400000
##                  Recall        F1 Prevalence Detection Rate
## Class: Sunny  0.8461538 0.8461538  0.2653061      0.2244898
## Class: Cloudy 0.7916667 0.8444444  0.2448980      0.1938776
## Class: Rainy  0.8400000 0.8235294  0.2551020      0.2142857
## Class: Snowy  0.9130435 0.8750000  0.2346939      0.2142857
##               Detection Prevalence Balanced Accuracy
## Class: Sunny             0.2653061         0.8952991
## Class: Cloudy            0.2142857         0.8823198
## Class: Rainy             0.2653061         0.8857534
## Class: Snowy             0.2551020         0.9298551
# Akurasi Multinomial
acc_mnl <- mean(pred_mnl == test_data$Weather_Type)
acc_mnl
## [1] 0.8469388

Pada tahap ini, kategori Summary ditetapkan sebagai kelas referensi sehingga seluruh jenis cuaca lainnya dibandingkan terhadap kondisi cuaca cerah. Selanjutnya dibangun model regresi logistik multinomial menggunakan variabel meteorologi seperti suhu, kelembapan, kecepatan angin, presipitasi, tekanan, UV index, dan visibilitas untuk memprediksi jenis cuaca. Hasil ringkasan model digunakan untuk melihat nilai koefisien serta p-value guna mengetahui seberapa besar dan signifikan pengaruh setiap variabel terhadap peluang munculnya masing-masing tipe cuaca. Setelah model terbentuk, dilakukan prediksi pada data uji untuk menilai kemampuan generalisasi model. Kinerja model kemudian dievaluasi menggunakan confusion matrix dan metrik performa per kelas, sehingga dapat diketahui seberapa baik model mengklasifikasikan tiap jenis cuaca. Terakhir, nilai akurasi dihitung untuk menunjukkan persentase keseluruhan prediksi yang berhasil dilakukan dengan benar oleh model.

Heatmap Confusion Matrix (Multinomial)

# Memastikan objek cm_mnl sudah tersedia
cm_mnl <- confusionMatrix(pred_mnl, test_data$Weather_Type)

# Visualisasi 7: Heatmap Confusion Matrix Multinomial
cm_mnl_df <- as.data.frame(cm_mnl$table)

ggplot(cm_mnl_df, aes(Reference, Prediction, fill = Freq)) +
  geom_tile() +
  geom_text(aes(label = Freq), color = "white") +
  scale_fill_gradient(low = "lightgreen", high = "darkgreen") +
  theme_minimal() +
  labs(title = "Heatmap Confusion Matrix - Multinomial",
       x = "Data Aktual (Reference)",
       y = "Data Prediksi (Prediction)")

Heatmap menampilkan confusion matrix model multinomial dalam bentuk visual. Warna hijau yang semakin gelap menunjukkan jumlah prediksi yang lebih banyak. Kotak diagonal menandakan prediksi yang benar, sedangkan kotak di luar diagonal menunjukkan kesalahan klasifikasi. Semakin dominan warna pada diagonal, semakin baik performa model.

Koefisien Variabenl LDA

# Visualisasi 8: Kontribusi Variabel LDA
lda_scaling <- as.data.frame(model_lda$scaling)
lda_scaling$Variable <- rownames(lda_scaling)
ggplot(lda_scaling, aes(x = reorder(Variable, LD1), y = LD1)) +
  geom_bar(stat = "identity", fill = "steelblue") +
  coord_flip() +
  theme_minimal() +
  labs(title = "Kontribusi Variabel terhadap LD1",
       x = "Variabel", y = "Koefisien LD1")

Visualisasi ini menunjukkan kontribusi setiap variabel terhadap fungsi diskriminan pertama (LD1) pada model LDA. Semakin besar nilai koefisien suatu variabel, semakin besar perannya dalam membedakan tipe cuaca. Variabel dengan batang paling panjang berarti paling berpengaruh dalam proses klasifikasi, sedangkan variabel dengan nilai kecil memiliki pengaruh yang lebih rendah terhadap pemisahan kelas.

Ringkasan Perbandingan Performa Model

Tabel Perbandingan Performa Model Klasifikasi Cuaca
Metode Analisis Akurasi (%) Akurasi (Proporsi) Indeks Kappa
Analisis Diskriminan (LDA) 80.61 % 0.8061 0.7416
Regresi Logistik Multinomial 84.69 % 0.8469 0.7958

Ringkasan Hasil Analisis

Berdasarkan seluruh tahapan analisis yang telah dilakukan, mulai dari eksplorasi data, pengujian asumsi, hingga evaluasi model, berikut adalah poin-poin kesimpulannya:

  1. Metode Regresi Logistik Multinomial menunjukkan performa yang lebih unggul dibandingkan Analisis Diskriminan Linier (LDA). Hal ini dibuktikan dengan nilai Akurasi sebesar 84.69% dan Indeks Kappa sebesar 0.7958.
  1. Model mampu membedakan tipe cuaca ekstrem (seperti Snowy) dengan sangat baik karena adanya perbedaan variabel suhu dan kelembapan yang signifikan.
  1. Untuk dataset cuaca dengan karakteristik non-linear atau yang tidak sepenuhnya memenuhi asumsi normalitas multivariat, penggunaan Regresi Logistik Multinomial lebih disarankan karena sifatnya yang lebih fleksibel (robust).

Laporan ini disusun sebagai bagian dari pemenuhan tugas praktikum mata kuliah Analisis Multivariat - Sains Data Universitas Negeri Surabaya.