Pendahuluan

Deskripsi Dataset

Dataset yang digunakan adalah jumlah kejadian bencana alam menurut jenis bencana dan kecamatan di Kota Malang periode 2016-2025, bersumber dari Badan Pusat Statistik (BPS) Kota Malang:

https://malangkota.bps.go.id/id/statistics-table/2/NDY0IzI=/jumlah-bencana-menurut-jenis-bencana-dan-kecamatan-di-kota-malang.html

Variabel yang dianalisis:

  • Tahun, Kecamatan (identitas observasi)
  • Longsor : jumlah kejadian tanah longsor
  • Cuaca : jumlah kejadian cuaca ekstrem
  • Banjir : jumlah kejadian banjir
  • Gempa : jumlah kejadian gempa bumi
  • Total : total seluruh kejadian bencana

Seluruh variabel numerik dianalisis secara deskriptif, dengan Banjir menjadi sorotan utama pembahasan karena merupakan bencana hidrometeorologis yang relevan dengan isu lingkungan.

Tujuan Analisis

Analisis ini bertujuan untuk:

  1. Mendeskripsikan karakteristik kejadian bencana di Kota Malang tahun 2016–2025.

  2. Mengidentifikasi pola distribusi, temporal, dan spasial kejadian bencana dengan fokus utama pada kejadian banjir.

  3. Mengidentifikasi observasi ekstrem menggunakan metode IQR dan Z-score.

  4. Memeriksa indikasi autokorelasi temporal menggunakan ACF dan autokorelasi spasial menggunakan Global Moran’s I.

Persiapan Package

library(readxl)
## Warning: package 'readxl' was built under R version 4.5.3
library(dplyr)
## 
## 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)
library(ggplot2)
library(moments)
library(sf)
## Linking to GEOS 3.13.1, GDAL 3.11.4, PROJ 9.7.0; sf_use_s2() is TRUE
library(spdep)
## Warning: package 'spdep' was built under R version 4.5.3
## Loading required package: spData
## Warning: package 'spData' was built under R version 4.5.3
## To access larger datasets in this package, install the spDataLarge
## package with: `install.packages('spDataLarge',
## repos='https://nowosad.github.io/drat/', type='source')`
## 
## Attaching package: 'spdep'
## The following object is masked from 'package:moments':
## 
##     geary
library(purrr)
library(knitr)

Input Data

df_raw <- read_excel("Data_Soal_Statistika_Lingkungan.xlsx")

df <- df_raw %>%
  select(
    Tahun     = matches("^Tahun$"),
    Kecamatan = matches("^Kecamatan$"),
    Longsor   = matches("Longsor"),
    Cuaca     = matches("Cuaca"),
    Banjir    = matches("^Banjir$"),
    Gempa     = matches("Gempa"),
    Total     = matches("Total")
  ) %>%
  as.data.frame()

df$Tahun <- as.integer(df$Tahun)
df$Kecamatan <- as.factor(df$Kecamatan)

str(df)
## 'data.frame':    50 obs. of  7 variables:
##  $ Tahun    : int  2016 2016 2016 2016 2016 2017 2017 2017 2017 2017 ...
##  $ Kecamatan: Factor w/ 5 levels "Blimbing","Kedungkandang",..: 2 5 3 1 4 2 5 3 1 4 ...
##  $ Longsor  : num  8 10 9 5 4 16 24 13 14 10 ...
##  $ Cuaca    : num  4 1 2 4 1 12 4 15 9 3 ...
##  $ Banjir   : num  0 0 1 1 0 0 2 3 4 4 ...
##  $ Gempa    : num  1 1 1 1 1 2 2 2 2 2 ...
##  $ Total    : num  13 12 13 11 6 30 32 33 29 19 ...
knitr::kable(head(df, 10), caption = "Contoh 10 baris pertama data bersih")
Contoh 10 baris pertama data bersih
Tahun Kecamatan Longsor Cuaca Banjir Gempa Total
2016 Kedungkandang 8 4 0 1 13
2016 Sukun 10 1 0 1 12
2016 Klojen 9 2 1 1 13
2016 Blimbing 5 4 1 1 11
2016 Lowokwaru 4 1 0 1 6
2017 Kedungkandang 16 12 0 2 30
2017 Sukun 24 4 2 2 32
2017 Klojen 13 15 3 2 33
2017 Blimbing 14 9 4 2 29
2017 Lowokwaru 10 3 4 2 19

Catatan Kualitas Data

cek_gempa <- df %>% pivot_wider(id_cols = Tahun, names_from = Kecamatan, values_from = Gempa)
knitr::kable(cek_gempa, caption = "Nilai Gempa per Kecamatan per Tahun - untuk mengecek variasi spasial")
Nilai Gempa per Kecamatan per Tahun - untuk mengecek variasi spasial
Tahun Kedungkandang Sukun Klojen Blimbing Lowokwaru
2016 1 1 1 1 1
2017 2 2 2 2 2
2018 3 3 3 3 3
2019 3 3 3 3 3
2020 2 2 2 2 2
2021 3 3 3 3 3
2022 1 1 1 1 1
2023 6 6 6 6 6
2024 3 3 3 3 3
2025 3 3 3 3 3

Nilai Gempa identik pada kelima kecamatan dalam tahun yang sama. Kesamaan tersebut mengindikasikan bahwa data gempa kemungkinan merepresentasikan nilai tingkat Kota Malang atau menggunakan pencatatan yang sama pada seluruh kecamatan. Oleh karena itu, variabel Gempa tidak digunakan dalam analisis yang membutuhkan variasi spasial antarwilayah, seperti Moran’s I.

Statistik Deskriptif Lengkap

Ringkasan Seluruh Variabel

r_iqr <- function(x) unname(quantile(x, 0.75) - quantile(x, 0.25))

deskriptif_semua <- df %>%
  select(Longsor, Cuaca, Banjir, Gempa, Total) %>%
  pivot_longer(everything(), names_to = "Variabel", values_to = "Nilai") %>%
  group_by(Variabel) %>%
  summarise(
    n = n(),
    Mean = round(mean(Nilai), 2),
    Median = median(Nilai),
    SD = round(sd(Nilai), 2),
    Min = min(Nilai),
    Max = max(Nilai),
    Q1 = round(quantile(Nilai, 0.25), 2),
    Q3 = round(quantile(Nilai, 0.75), 2),
    IQR = round(r_iqr(Nilai), 2),
    Skewness = round(skewness(Nilai), 3),
    .groups = "drop"
  )
knitr::kable(deskriptif_semua, caption = "Statistik deskriptif seluruh variabel bencana")
Statistik deskriptif seluruh variabel bencana
Variabel n Mean Median SD Min Max Q1 Q3 IQR Skewness
Banjir 50 14.36 10.5 15.03 0 66 2.00 21.00 19.00 1.270
Cuaca 50 9.10 8.5 7.49 1 42 3.25 12.75 9.50 1.842
Gempa 50 2.70 3.0 1.36 1 6 2.00 3.00 1.00 1.050
Longsor 50 12.02 10.5 6.71 4 33 8.00 14.00 6.00 1.385
Total 50 38.18 32.5 21.85 6 108 23.25 50.50 27.25 0.990

Banjir

Rata-rata kejadian banjir 14,36 kejadian per kecamatan per tahun. Artinya, jika seluruh 50 observasi dirata-ratakan, setiap observasi kecamatan pada suatu tahun mengalami sekitar 14 kejadian banjir. Namun, median hanya 10,5 kejadian. Ini memberikan indikasi bahwa beberapa observasi jumlah banjir sangat tinggi membuat rata-rata tertarik ke atas.

Hal tersebut terlihat juga pada nilai minimum yang bernilai 0, dan nilai maksimum sebesar 66. IQR sebesar 19 menunjukkan bahwa 50% data tengah tersebar dalam rentang 19 kejadian. Nilai SD sebesar 15,03, lebih besar daripada mean. Ini menunjukkan bahwa jumlah kejadian banjir mempunyai variabilitas yang cukup tinggi antar kecamatan-tahun.

Skewness 1,270 menunjukkan distribusi menceng positif. Dimana banyak kecamatan-tahun memiliki jumlah banjir yang relatif rendah, tetapi beberapa kecamatan-tahun mengalami jumlah banjir yang jauh lebih tinggi.

Cuaca

Rata-rata kejadian bencana Cuaca adalah 9,10 kejadian per kecamatan-tahun, sedangkan median sebesar 8,5 kejadian. Nilai minimum 1, sementara maksimum 42. IQR sebesar 9,50 menunjukkan variasi pada 50% data tengah relatif lebih kecil dibandingkan banjir.

SD sebesar 7,49 juga menunjukkan adanya variasi jumlah kejadian cuaca antar kecamatan-tahun. Nilai skewness sebesar 1,842, yang merupakan nilai skewness terbesar dari empat jenis bencana. Artinya, distribusi kejadian cuaca sangat menceng ke kanan dibandingkan variabel lainnya. Jadi, kejadian cuaca merupakan variabel yang distribusinya paling tidak simetris.

Gempa

Gempa memiliki rata-rata 2,70 kejadian per kecamatan-tahun, dengan median 3 kejadian. Berbeda dengan banjir dan cuaca, di sini mean sedikit lebih kecil daripada median. Ini menunjukkan bahwa terdapat kecenderungan distribusi yang berbeda. Jumlah kejadian gempa relatif lebih rendah, 50% observasi tengah hanya berada pada rentang 2–3 kejadian. IQR hanya 1, sehingga variasi pada 50% data tengah sangat kecil.

SD sebesar 1,36, dengan demikian, dibandingkan jenis bencana lainnya, jumlah kejadian gempa relatif lebih stabil antar observasi. Skewness 1,050 menunjukkan distribusi masih menceng positif, tetapi kemencengannya tidak sekuat variabel cuaca.

Longsor

Rata-rata kejadian longsor adalah 12,02 kejadian per kecamatan-tahun, dengan median 10,5 kejadian. Nilai minimum 4 menunjukkan bahwa seluruh observasi memiliki setidaknya 4 kejadian longsor. Maksimum 33 kejadian menunjukkan adanya kecamatan-tahun tertentu dengan jumlah longsor yang cukup tinggi. IQR sebesar 6, sehingga variasi 50% data tengah relatif lebih kecil dibandingkan banjir dan cuaca. SD bernilai 6,71 menunjukkan variasi tetap ada, tetapi tidak sebesar variasi pada banjir. Skewness 1,385 menunjukkan distribusi menceng ke kanan.

Jumlah Keseluruhan Bencana

Secara keseluruhan, total kejadian bencana memiliki rata-rata sebesar 38,18 kejadian per kecamatan-tahun, dengan median sebesar 32,50 kejadian. Nilai minimum dan maksimum masing-masing sebesar 6 dan 108 kejadian, sedangkan IQR sebesar 27,25 menunjukkan adanya variasi yang cukup besar pada total kejadian bencana antar kecamatan-tahun. Perbedaan antara mean dan median serta nilai skewness positif menunjukkan bahwa terdapat beberapa kecamatan-tahun dengan jumlah kejadian bencana yang relatif tinggi. Dengan demikian, karakteristik data menunjukkan bahwa kejadian bencana tidak terdistribusi secara merata antar kecamatan dan tahun, dengan banjir menjadi jenis bencana yang memiliki rata-rata kejadian paling tinggi serta variasi yang paling besar di antara jenis bencana yang dianalisis.

Fokus Variabel Banjir

ringkasan_kec <- df %>% group_by(Kecamatan) %>%
  summarise(Mean = round(mean(Banjir),2), Median = median(Banjir),
            SD = round(sd(Banjir),2), Min = min(Banjir), Max = max(Banjir), .groups="drop") %>%
  arrange(desc(Mean))
knitr::kable(ringkasan_kec, caption = "Rata-rata kejadian Banjir per Kecamatan (2016-2025)")
Rata-rata kejadian Banjir per Kecamatan (2016-2025)
Kecamatan Mean Median SD Min Max
Klojen 17.8 9.0 21.41 1 66
Lowokwaru 17.5 15.5 15.43 0 52
Blimbing 15.6 13.5 13.34 1 35
Sukun 10.6 8.0 12.47 0 40
Kedungkandang 10.3 7.0 11.86 0 35
ringkasan_tahun <- df %>% group_by(Tahun) %>%
  summarise(Total_Banjir = sum(Banjir), Mean = round(mean(Banjir),2), SD = round(sd(Banjir),2), .groups="drop")
knitr::kable(ringkasan_tahun, caption = "Total & rata-rata kejadian Banjir per Tahun (seluruh kecamatan)")
Total & rata-rata kejadian Banjir per Tahun (seluruh kecamatan)
Tahun Total_Banjir Mean SD
2016 2 0.4 0.55
2017 13 2.6 1.67
2018 9 1.8 1.92
2019 14 2.8 3.83
2020 34 6.8 3.77
2021 92 18.4 9.02
2022 98 19.6 8.20
2023 224 44.8 14.24
2024 115 23.0 6.96
2025 117 23.4 8.50

Klojen memiliki rata-rata kejadian tertinggi (17,8) dan SD tertinggi (21,41), sehingga kecamatan ini memiliki jumlah kejadian banjir yang tinggi. Sementara itu, Lowokwaru memiliki rata-rata hampir sama (17,5), tetapi median yang lebih tinggi (15,5). Hal ini menunjukkan bahwa kejadian banjir di Lowokwaru cenderung lebih konsisten dibandingkan Klojen.

Terjadi peningkatan kejadian banjir yang sangat jelas mulai 2020, kemudian mencapai puncak pada 2023 dengan total 224 kejadian. Setelah itu terjadi penurunan pada 2024 menjadi 115 kejadian dan sedikit peningkatan menjadi 117 kejadian pada 2025. Dengan demikian, data menunjukkan bahwa kejadian banjir memiliki variasi baik antar-kecamatan maupun antar-tahun. Kecamatan dengan rata-rata tinggi tidak selalu memiliki pola yang sama, sementara perubahan antar-tahun menunjukkan adanya periode peningkatan yang sangat tajam terutama pada 2021–2023.

Visualisasi Data

1. Histogram

df_long <- df %>% pivot_longer(c(Longsor, Cuaca, Banjir, Gempa), names_to = "Variabel", values_to = "Nilai")

ggplot(df_long, aes(x = Nilai, fill = Variabel)) +
  geom_histogram(bins = 10, color = "white", show.legend = FALSE) +
  facet_wrap(~Variabel, scales = "free") +
  labs(title = "Distribusi Setiap Jenis Kejadian Bencana (2016-2025)",
       x = "Jumlah Kejadian", y = "Frekuensi") +
  theme_minimal()

Banjir

Histogram untuk variabel banjir menunjukkan distribusi yang menceng ke kanan, menunjukkan sebagian besar observasi ada pada jumlah kerjadian yang relatif rendah. Frekuensi semakin menurun ketika jumlah kejadian meningkat. Namun terdapat beberapa observasi dengan jumlah kejadian yang jauh lebih tinggi, mencapai angka sekitar 60 kejadian. nilai tersebut membentuk ekor panjang di sisi kanan.

Cuaca

Histogram Cuaca juga menunjukkan pola ke kanan, meskipun bentuknya sedikit berbeda dengan banjir. Data cukup banyak tersebar pada rentang 0-20 kejadian, setelah jumlah kejadian melewati sekitar 15-20, frekuensi mulai menurun. Terdapat beberapa observasi dengan jumlah kejadian yang lebih tinggi, termasuk satu observasi di sekitar 40 kejadian. Nilai tersebut memperpanjang ekor distribusi ke kanan.

Gempa

Untuk Variabel gempa terlihat berbeda dibandingkan 3 variabel lainnya. Data terpusat pada jumlah kejadian yang sangat kecil, yaitu sampai 1-3 kejadian. Frekuensi tertinggi berada pada 3 kejadian. Hanya sebagian kecil observasi yang mencapai jumlah kejadian lebih tinggi.

Longsor

Histogram longsor menunjukkan bahwa sebagian besar observasi terpusat pada 5-15 kejadian, dengan frekuensi tertinggi disekitar 10-15 kejadian. Masih terdapat beberapa observasi dengan jumlah kejadian lebih dari 20. Hal ini membentuk ekor kekanan.

2. Boxplot

ggplot(df, aes(x = reorder(Kecamatan, Banjir, median), y = Banjir, fill = Kecamatan)) +
  geom_boxplot(outlier.color = "red", outlier.size = 2.5, show.legend = FALSE) +
  geom_jitter(width = 0.1, alpha = 0.5, size = 1.3) +
  labs(title = "Sebaran Kejadian Banjir per Kecamatan (2016-2025)",
       x = "Kecamatan", y = "Jumlah Kejadian Banjir") +
  theme_minimal()

Klojen

Klojen menjadi kecamatan yang memiliki kejadian banjir relatif tinggi, tetapi penyebarannya juga besar. Boxnya terlihat paling tinggi. Klojen tidak hanya memiliki rata-rata banjir yang tinggi, tetapi juga mengalami beberapa tahun dengan kejadian yang ekstrem.

Lowokwaru

Lowokwaru memilki median sekitar sekitar 15 kejadian, yang merupakan salah satu median tertinggi dibandingkan kecamatan lainnya. Penyebaran datanya juga cukup besar, ditunjukkan dengan adanya outlier sekitar 50 kejadian. artianya pada sebagian besar tahun jumlah kejadian banjir berada pada tingkat yang relatif tinggi, tetapi terdapat satu periode dengan kejadian yang lebih besar.

Blimbing

Median pada kecamatan Blimbing sedikit lebih kecil dari kecamatan lowakwaru yaitu sekitar 13 kejadian, terdapat nilai maksimum sekitar 35 kejadian, yang menunjukkan adanya tahun tertentu dengan peningkatan kejadian banjir.

Sukun

Sukun dengan median sekitar 8 kejadian dan penyebaran yang cukup lebar, adanya nilai tinggi disekitar 40 kejadian terlihat sebagai outlier.

Kedungkandang

Kedungkandang memiliki median sekitar 7 kejadian, dan termasuk kecamatan dengan tingkat kejadian banjir yang relatif rendah. Namun ada observasi dengan kejadian 35 kejadian, menunjukkan adanya periode tertentu dengan lonjakan kejadian.

3. Time Series

ggplot(df, aes(x = Tahun, y = Banjir, color = Kecamatan)) +
  geom_line(linewidth = 1) + geom_point(size = 2) +
  scale_x_continuous(breaks = 2016:2025) +
  labs(title = "Tren Kejadian Banjir per Kecamatan, 2016-2025", x = "Tahun", y = "Jumlah Kejadian Banjir") +
  theme_minimal()

Grafik menunjukkan kejadian banjir pada lima kecamatan mengalami fluktuasi selama periode 2016 sampai 2025, pada tahun 2016-2020 jumlah kejadian banjir pada seluruh kecamatan masih rendah. Pada tahun 2021 terdapat perubahan pola yang sangat jelas. Klojen mengalami peningkatan tajam hingga 32 kejadian, lowakwaru juga meningkat sekitar 20 kejadian. Blimbing dan Sukun juga mengalami peningkatan. Hanya kendungkandang yang kejadiannya menurundi tahun 2021.

Pada 2022, pola antar kecamatan semakin bervariasi. Blimbing meningkat hingga sekitar 32 kejadian, sementara Lowokwaru berada di sekitar 24 kejadian. Sebaliknya, Klojen mengalami penurunan dibandingkan 2021. Hal ini menunjukkan bahwa kenaikan kejadian banjir tidak berlangsung secara seragam pada seluruh kecamatan. Tahun 2023 merupakan periode paling menonjol, Seluruh kecamatan mengalami peningkatan.

Setelah mencapai puncaknya pada 2023, jumlah kejadian banjir menurun pada hampir seluruh kecamatan pada 2024. Pada 2025, pola antar kecamatan mulai berbeda. Blimbing dan Lowokwaru kembali mengalami peningkatan, sedangkan Klojen, Kedungkandang, dan Sukun cenderung menurun.

ts_total <- df %>% group_by(Tahun) %>%
  summarise(Longsor = sum(Longsor), Cuaca = sum(Cuaca), Banjir = sum(Banjir),
            Gempa = sum(Gempa), Total = sum(Total), .groups = "drop") %>%
  pivot_longer(-Tahun, names_to = "Variabel", values_to = "Jumlah")

ggplot(ts_total, aes(x = Tahun, y = Jumlah, color = Variabel)) +
  geom_line(linewidth = 1.1) + geom_point(size = 2) +
  scale_x_continuous(breaks = 2016:2025) +
  labs(title = "Total Setiap Jenis Bencana Se-Kota Malang per Tahun", x = "Tahun", y = "Total Kejadian") +
  theme_minimal()

Grafik ini memperlihatkan perkembangan Banjir, Cuaca, Gempa, Longsor, dan Total kejadian bencana setiap tahun. Banjir menunjukkan fluktuasi paling besar dan mencapai puncak sebesar 200 an kejadian pada tahun 2023, kemudian menurun menjadi 115 kejadian pada tahun 2024 dan sedikit meningkat pada tahun 2025. Kejadian cuaca menunjukkan kecenderungan peningkatan hingga mencapai puncaknya pada tahun 2024 sebelum menurun pada tahun 2025. Longsor menunjukkan pola yang lebih berfluktuasi dan kembali mengalami peningkatan pada tahun 2025, sedangkan gempa relatif lebih rendah dan stabil sepanjang periode pengamatan. Secara keseluruhan, total kejadian bencana mencapai tingkat tertinggi pada tahun 2023, kemudian mengalami penurunan pada tahun 2024 dan sedikit meningkat pada tahun 2025.

Sebaran Spasial Rata-Rata Kejadian Banjir

admin_desa <- st_read("ADMINISTRASIDESA_AR_25K.shp", quiet = TRUE)

kota_malang_sf <- admin_desa %>%
  filter(toupper(trimws(WADMKK)) == "KOTA MALANG")

cat("Kecamatan pada shapefile:", paste(sort(unique(trimws(kota_malang_sf$WADMKC))), collapse = ", "), "\n")
## Kecamatan pada shapefile: BLIMBING, KEDUNGKANDANG, KLOJEN, LOWOKWARU, SUKUN
# Dissolve kelurahan -> kecamatan
kec_sf <- kota_malang_sf %>%
  mutate(Kecamatan = tools::toTitleCase(tolower(trimws(WADMKC)))) %>%
  group_by(Kecamatan) %>%
  summarise(geometry = st_union(geometry), .groups = "drop") %>%
  st_make_valid()

plot(st_geometry(kec_sf), main = "Batas 5 Kecamatan Kota Malang (hasil dissolve)")

peta_data <- df %>%
  group_by(Kecamatan) %>%
  summarise(Mean_Banjir = mean(Banjir), .groups = "drop")

peta_data_sf <- kec_sf %>% left_join(peta_data, by = "Kecamatan")

ggplot(peta_data_sf) +
  geom_sf(aes(fill = Mean_Banjir), color = "white", linewidth = 0.4) +
  geom_sf_text(aes(label = Kecamatan), size = 3.6, color = "black", fontface = "bold") +
  scale_fill_gradient(low = "orange", high = "red") +
  labs(title = "Rata-Rata Kejadian Banjir per Kecamatan (2016-2025)",
       subtitle = "Batas administrasi resmi RBI 25K (BIG), Kota Malang",
       fill = "Rata-rata\nBanjir") +
  theme_minimal() +
  theme(axis.text = element_blank(), axis.ticks = element_blank(), panel.grid = element_blank())
## Warning in st_point_on_surface.sfc(sf::st_zm(x)): st_point_on_surface may not
## give correct results for longitude/latitude data

Berdasarkan peta spasial rata-rata kejadian banjir di Kota Malang selama periode 2016–2025, terlihat adanya perbedaan rata-rata kejadian banjir antar kecamatan. Kecamatan Klojen memiliki rata-rata kejadian banjir tertinggi sebesar 17,8 kejadian per tahun, diikuti oleh Lowokwaru sebesar 17,5 kejadian dan Blimbing sebesar 15,6 kejadian. Sementara itu, Kecamatan Sukun dan Kedungkandang memiliki rata-rata yang relatif lebih rendah, masing-masing sebesar 10,6 dan 10,3 kejadian per tahun. Secara spasial, kecamatan dengan rata-rata kejadian banjir relatif tinggi, yaitu Klojen, Lowokwaru, dan Blimbing, berada pada wilayah yang saling berdekatan di bagian tengah hingga utara Kota Malang. Sebaliknya, Sukun dan Kedungkandang yang berada pada bagian selatan menunjukkan rata-rata kejadian yang relatif lebih rendah. Pola tersebut secara memberikan petunjuk adanya perbedaan karakteristik kejadian banjir berdasarkan lokasi geografis.

Deteksi dan Interpretasi Outlier

Metode IQR menggunakan batas Q1 − 1,5×IQR dan Q3 + 1,5×IQR, sedangkan Z-score menggunakan kriteria |Z| > 2.

deteksi_outlier <- function(data, var) {
  x <- data[[var]]
  Q1 <- unname(quantile(x, 0.25, na.rm = TRUE))
Q3 <- unname(quantile(x, 0.75, na.rm = TRUE))
  IQR_val <- IQR(x, na.rm = TRUE)
  
  batas_bawah <- Q1 - 1.5 * IQR_val
  batas_atas  <- Q3 + 1.5 * IQR_val
  
  mean_x <- mean(x, na.rm = TRUE)
  sd_x   <- sd(x, na.rm = TRUE)
  z_score <- (x - mean_x) / sd_x
  outlier_iqr <- x < batas_bawah | x > batas_atas
  outlier_z   <- abs(z_score) > 2
  idx <- which(outlier_iqr | outlier_z)
  
  if (length(idx) == 0) {
    return(NULL)
  }
  
  data.frame(
    Variabel = var,
    Tahun = data$Tahun[idx],
    Kecamatan = data$Kecamatan[idx],
    Nilai = x[idx],
    
    Q1 = round(Q1, 2),
    Q3 = round(Q3, 2),
    IQR = round(IQR_val, 2),
    
    `Batas Bawah` = round(batas_bawah, 2),
    `Batas Atas` = round(batas_atas, 2),
    
    `Z-score` = round(z_score[idx], 2),
    
    `Outlier IQR` = ifelse(
      outlier_iqr[idx],
      "Ya",
      "Tidak"
    ),
    
    `Outlier Z-score` = ifelse(
      outlier_z[idx],
      "Ya",
      "Tidak"
    ),
    
    Metode = case_when(
      outlier_iqr[idx] & outlier_z[idx] ~ "IQR dan Z-score",
      outlier_iqr[idx] ~ "IQR saja",
      outlier_z[idx] ~ "Z-score saja"
    ),
    
    check.names = FALSE
  )
}

outlier_all <- map_dfr(
  c("Longsor", "Cuaca", "Banjir", "Gempa", "Total"),
  ~ deteksi_outlier(df, .x)
)

outlier_ringkas <- outlier_all %>%
  select(
    Variabel,
    Tahun,
    Kecamatan,
    Nilai,
    `Z-score`,
    `Outlier IQR`,
    `Outlier Z-score`,
    Metode
  )

knitr::kable(
  outlier_ringkas,
  caption = "Observasi yang Terdeteksi sebagai Outlier",
  row.names = FALSE
)
Observasi yang Terdeteksi sebagai Outlier
Variabel Tahun Kecamatan Nilai Z-score Outlier IQR Outlier Z-score Metode
Longsor 2017 Sukun 24 1.79 Ya Tidak IQR saja
Longsor 2021 Sukun 26 2.08 Ya Ya IQR dan Z-score
Longsor 2022 Sukun 31 2.83 Ya Ya IQR dan Z-score
Longsor 2022 Blimbing 27 2.23 Ya Ya IQR dan Z-score
Longsor 2025 Sukun 33 3.13 Ya Ya IQR dan Z-score
Cuaca 2024 Klojen 42 4.39 Ya Ya IQR dan Z-score
Banjir 2023 Klojen 66 3.44 Ya Ya IQR dan Z-score
Banjir 2023 Lowokwaru 52 2.50 Ya Ya IQR dan Z-score
Gempa 2023 Kedungkandang 6 2.43 Ya Ya IQR dan Z-score
Gempa 2023 Sukun 6 2.43 Ya Ya IQR dan Z-score
Gempa 2023 Klojen 6 2.43 Ya Ya IQR dan Z-score
Gempa 2023 Blimbing 6 2.43 Ya Ya IQR dan Z-score
Gempa 2023 Lowokwaru 6 2.43 Ya Ya IQR dan Z-score
Total 2023 Klojen 108 3.20 Ya Ya IQR dan Z-score
Total 2023 Lowokwaru 82 2.01 Tidak Ya Z-score saja
Total 2024 Klojen 88 2.28 Tidak Ya Z-score saja

Berdasarkan hasil deteksi outlier menggunakan metode IQR dan Z-score, ditemukan beberapa observasi ekstrem pada variabel kejadian bencana, terutama pada kejadian longsor, banjir, gempa, dan total kejadian. Tahun 2023 menjadi periode yang paling dominan dalam kemunculan outlier, khususnya pada variabel banjir, gempa, dan total. Pada kejadian banjir, Klojen dan Lowokwaru tahun 2023 teridentifikasi sebagai outlier dengan nilai masing-masing 66 dan 52 kejadian. Pada variabel gempa, seluruh kecamatan pada tahun 2023 memiliki nilai 6 kejadian dan terdeteksi sebagai outlier. Sementara itu, kejadian cuaca paling ekstrem ditemukan di Klojen tahun 2024 dengan nilai 42 kejadian dan Z-score sebesar 4,39. Untuk longsor, outlier paling sering muncul di Kecamatan Sukun, terutama pada tahun 2021, 2022, dan 2025. Secara keseluruhan, pola outlier menunjukkan bahwa kejadian ekstrem tidak terjadi secara acak, tetapi terkonsentrasi pada tahun dan kecamatan tertentu.

Pemeriksaan Autokorelasi

Autokorelasi Temporal (ACF)

ts_banjir_tahun <- df %>% group_by(Tahun) %>% summarise(Total_Banjir = sum(Banjir), .groups="drop")
ts_banjir <- ts(ts_banjir_tahun$Total_Banjir, start = 2016, frequency = 1)
print(ts_banjir)
## Time Series:
## Start = 2016 
## End = 2025 
## Frequency = 1 
##  [1]   2  13   9  14  34  92  98 224 115 117
acf(ts_banjir, lag.max = 5, main = "ACF - Total Kejadian Banjir per Tahun (2016-2025)")

Tidak terdapat autokorelasi temporal yang signifikan pada lag 1 sampai lag 5. Meskipun lag 1 menunjukkan korelasi positif yang cukup besar secara deskriptif, nilainya masih belum melewati batas signifikansi. Dengan demikian, total kejadian banjir tahunan 2016–2025 belum menunjukkan pola ketergantungan waktu yang kuat berdasarkan grafik ACF. Interpretasi ACF dilakukan secara eksploratif karena hanya tersedia 10 observasi tahunan (2016–2025), sehingga kemampuan untuk mendeteksi struktur autokorelasi relatif terbatas.

Autokorelasi Spasial (Moran’s I)

# Rata-rata banjir per kecamatan
peta_data <- df %>%
  group_by(Kecamatan) %>%
  summarise(
    Mean_Banjir = mean(Banjir),
    .groups = "drop"
  )

# Join dengan peta
kec_sf_moran <- kec_sf %>%
  left_join(peta_data, by = "Kecamatan")

# Tetangga spasial
nb <- poly2nb(
  kec_sf_moran,
  queen = TRUE,
  row.names = kec_sf_moran$Kecamatan
)

# Bobot spasial
listw <- nb2listw(
  nb,
  style = "W",
  zero.policy = TRUE
)

# Global Moran's I
moran_result <- moran.test(
  kec_sf_moran$Mean_Banjir,
  listw,
  zero.policy = TRUE
)

moran_result
## 
##  Moran I test under randomisation
## 
## data:  kec_sf_moran$Mean_Banjir  
## weights: listw    
## 
## Moran I statistic standard deviate = 0.75117, p-value = 0.2263
## alternative hypothesis: greater
## sample estimates:
## Moran I statistic       Expectation          Variance 
##       -0.09948253       -0.25000000        0.04015131

Hasil pengujian Global Moran’s I terhadap rata-rata kejadian banjir antar kecamatan menghasilkan nilai Moran’s I sebesar −0,0995 dengan nilai harapan sebesar −0,2500 dan p-value sebesar 0,2263. Pada taraf signifikansi 5%, p-value yang lebih besar dari 0,05 menyebabkan hipotesis nol tidak ditolak. Dengan demikian, belum terdapat bukti statistik yang cukup untuk menyatakan adanya autokorelasi spasial positif yang signifikan pada rata-rata kejadian banjir antar kecamatan di Kota Malang selama periode 2016–2025. Meskipun peta spasial secara visual menunjukkan perbedaan rata-rata kejadian banjir antarwilayah, pola tersebut belum dapat dinyatakan sebagai pengelompokan spasial yang signifikan berdasarkan Global Moran’s I. Hasil ini perlu diinterpretasikan secara hati-hati karena analisis hanya melibatkan lima kecamatan, sehingga kemampuan pengujian untuk mendeteksi pola spasial relatif terbatas.

Kesimpulan

Berdasarkan eksplorasi data kejadian bencana di Kota Malang periode 2016–2025, banjir merupakan jenis bencana dengan rata-rata kejadian tertinggi dan variasi terbesar dibandingkan jenis bencana lainnya. Kecamatan Klojen dan Lowokwaru memiliki rata-rata kejadian banjir tertinggi, sedangkan secara temporal terjadi peningkatan kejadian yang menonjol hingga mencapai puncak pada tahun 2023. Analisis outlier menunjukkan bahwa beberapa kejadian ekstrem terkonsentrasi pada tahun dan kecamatan tertentu, terutama pada tahun 2023. Pemeriksaan ACF belum menunjukkan autokorelasi temporal yang signifikan pada total kejadian banjir tahunan, sedangkan Global Moran’s I juga belum memberikan bukti yang cukup mengenai autokorelasi spasial yang signifikan pada rata-rata kejadian banjir antar kecamatan. Secara keseluruhan, data menunjukkan adanya variasi kejadian bencana yang kuat secara temporal dan antarwilayah, tetapi pola tersebut belum menunjukkan ketergantungan temporal maupun spasial yang signifikan berdasarkan analisis yang dilakukan.