Eksplorasi Data Lingkungan: Volume Sampah Sungai di DKI Jakarta

1 Pendahuluan

Sampah sungai merupakan salah satu permasalahan lingkungan yang berkaitan dengan kualitas ekosistem perairan, sistem drainase, serta pengelolaan sampah perkotaan. Volume sampah yang ditemukan pada badan sungai dapat berubah dari waktu ke waktu dan berbeda antarwilayah.

Data yang digunakan merupakan data lingkungan aktual dari Portal Data Terbuka Pemerintah Provinsi DKI Jakarta.

Data utama yang digunakan adalah dataset Rata-Rata Volume Sampah Sungai di Provinsi DKI Jakarta Menurut Kota Administrasi.

Analisis meliputi statistik deskriptif, visualisasi, deteksi outlier, autokorelasi temporal menggunakan ACF, serta analisis spasial menggunakan Moran’s I dan Local Moran’s I.

2 Tujuan Analisis

  1. Mendeskripsikan karakteristik volume sampah sungai di DKI Jakarta.
  2. Membandingkan volume sampah antarwilayah.
  3. Mengidentifikasi outlier menggunakan metode IQR.
  4. Mengamati pola perubahan volume sampah secara temporal.
  5. Menguji autokorelasi temporal menggunakan ACF.
  6. Menguji pola spasial menggunakan Moran’s I.
  7. Memvisualisasikan distribusi volume sampah pada tingkat kecamatan.

3 Persiapan Data

3.1 Direktori Kerja

setwd("C:/Users/sambe/Downloads/TUGAS 1 STAT LINGKUNGAN")
getwd()
## [1] "C:/Users/sambe/Downloads/TUGAS 1 STAT LINGKUNGAN"

3.2 Import Data

file_data <- "Rata-Rata_Volume Sampah Sungai di Provinsi DKI Jakarta Menurut Kota Administrasi.xls"

tabel_data <- read_html(file_data) |>
  html_table(fill = TRUE)

data_raw <- tabel_data[[1]]

3.3 Cleaning Data

data <- data_raw |>
  clean_names() |>
  mutate(
    periode_data = as.character(periode_data),
    tanggal = ymd(paste0(periode_data, "01")),
    wilayah = str_squish(wilayah),
    kecamatan = str_squish(kecamatan),
    volume_sampah = as.numeric(volume_sampah),
    total_sampah_residu = as.numeric(total_sampah_residu),
    rata_rata = as.numeric(rata_rata)
  ) |>
  filter(!is.na(rata_rata))

3.4 Struktur Data

glimpse(data)
## Rows: 1,302
## Columns: 7
## $ periode_data        <chr> "202401", "202401", "202401", "202401", "202401", …
## $ wilayah             <chr> "KOTA ADM. JAKARTA PUSAT", "KOTA ADM. JAKARTA PUSA…
## $ kecamatan           <chr> "KEMAYORAN", "CEMPAKA PUTIH", "JOHAR BARU", "SENEN…
## $ volume_sampah       <dbl> 999.04, 363.91, 715.29, 500.13, 693.33, 841.22, 68…
## $ total_sampah_residu <dbl> 997.0, 363.0, 714.0, 498.0, 692.0, 837.0, 682.0, 7…
## $ rata_rata           <dbl> 32.16, 11.71, 23.03, 16.06, 22.32, 27.00, 22.00, 2…
## $ tanggal             <date> 2024-01-01, 2024-01-01, 2024-01-01, 2024-01-01, 2…
data |>
  summarise(
    jumlah_observasi = n(),
    jumlah_kecamatan = n_distinct(kecamatan),
    jumlah_kota = n_distinct(wilayah),
    periode_awal = min(tanggal, na.rm = TRUE),
    periode_akhir = max(tanggal, na.rm = TRUE)
  ) |>
  kable(caption = "Ringkasan Struktur Data") |>
  kable_styling(full_width = FALSE)
Ringkasan Struktur Data
jumlah_observasi jumlah_kecamatan jumlah_kota periode_awal periode_akhir
1302 46 5 2024-01-01 2026-07-01

3.5 Distribusi Observasi Berdasarkan Kota

data |>
  count(wilayah, sort = TRUE) |>
  kable(
    col.names = c("Kota Administrasi", "Jumlah Observasi"),
    caption = "Jumlah Observasi Berdasarkan Kota Administrasi"
  ) |>
  kable_styling(full_width = FALSE)
Jumlah Observasi Berdasarkan Kota Administrasi
Kota Administrasi Jumlah Observasi
KOTA ADM. JAKARTA SELATAN 310
KOTA ADM. JAKARTA TIMUR 310
KOTA ADM. JAKARTA BARAT 248
KOTA ADM. JAKARTA PUSAT 248
KOTA ADM. JAKARTA UTARA 186

4 Statistik Deskriptif

Statistik deskriptif digunakan untuk mengetahui kecenderungan pusat dan tingkat penyebaran volume sampah sungai. Ukuran yang digunakan meliputi mean, median, standar deviasi, varians, minimum, kuartil, IQR, dan maksimum.

4.1 Statistik Keseluruhan

statistik_deskriptif <- data |>
  summarise(
    N = n(),
    Mean = mean(rata_rata, na.rm = TRUE),
    Median = median(rata_rata, na.rm = TRUE),
    SD = sd(rata_rata, na.rm = TRUE),
    Varians = var(rata_rata, na.rm = TRUE),
    Minimum = min(rata_rata, na.rm = TRUE),
    Q1 = quantile(rata_rata, 0.25, na.rm = TRUE),
    Q3 = quantile(rata_rata, 0.75, na.rm = TRUE),
    IQR = IQR(rata_rata, na.rm = TRUE),
    Maksimum = max(rata_rata, na.rm = TRUE)
  )

statistik_deskriptif |>
  mutate(across(where(is.numeric), ~ round(.x, 2))) |>
  kable(caption = "Statistik Deskriptif Volume Sampah Sungai") |>
  kable_styling(full_width = FALSE)
Statistik Deskriptif Volume Sampah Sungai
N Mean Median SD Varians Minimum Q1 Q3 IQR Maksimum
1302 30.38 20.71 35.82 1282.83 0.47 15.6 29.55 13.95 316.87

4.2 Statistik Berdasarkan Kota

statistik_kota <- data |>
  group_by(wilayah) |>
  summarise(
    N = n(),
    Mean = mean(rata_rata, na.rm = TRUE),
    Median = median(rata_rata, na.rm = TRUE),
    SD = sd(rata_rata, na.rm = TRUE),
    Minimum = min(rata_rata, na.rm = TRUE),
    Q1 = quantile(rata_rata, 0.25, na.rm = TRUE),
    Q3 = quantile(rata_rata, 0.75, na.rm = TRUE),
    Maksimum = max(rata_rata, na.rm = TRUE),
    .groups = "drop"
  ) |>
  mutate(across(where(is.numeric), ~ round(.x, 2)))

statistik_kota |>
  kable(
    col.names = c("Kota Administrasi", "N", "Mean", "Median", "SD",
                  "Minimum", "Q1", "Q3", "Maksimum"),
    caption = "Statistik Deskriptif Berdasarkan Kota Administrasi"
  ) |>
  kable_styling(full_width = FALSE)
Statistik Deskriptif Berdasarkan Kota Administrasi
Kota Administrasi N Mean Median SD Minimum Q1 Q3 Maksimum
KOTA ADM. JAKARTA BARAT 248 38.07 30.45 33.21 10.63 24.13 42.14 233.86
KOTA ADM. JAKARTA PUSAT 248 25.35 19.67 32.51 1.01 15.38 23.41 208.11
KOTA ADM. JAKARTA SELATAN 310 27.84 17.11 40.96 0.47 13.27 20.59 316.87
KOTA ADM. JAKARTA TIMUR 310 27.50 18.31 34.02 0.64 13.43 27.20 242.89
KOTA ADM. JAKARTA UTARA 186 35.90 24.26 35.12 0.89 19.59 40.09 239.00

5 Visualisasi

5.1 Histogram

ggplot(data, aes(x = rata_rata)) +
  geom_histogram(bins = 40, fill = "steelblue", color = "white") +
  geom_vline(
    aes(xintercept = mean(rata_rata, na.rm = TRUE)),
    linetype = "dashed", linewidth = 1
  ) +
  labs(
    title = "Distribusi Rata-Rata Volume Sampah Sungai",
    subtitle = "DKI Jakarta, 2024–2026",
    x = "Rata-rata Volume Sampah",
    y = "Frekuensi"
  ) +
  theme_minimal()

5.2 Boxplot

ggplot(data, aes(y = rata_rata)) +
  geom_boxplot(fill = "lightblue", outlier.alpha = 0.5) +
  labs(
    title = "Boxplot Rata-Rata Volume Sampah Sungai",
    x = NULL,
    y = "Rata-rata Volume Sampah"
  ) +
  theme_minimal()

5.3 Boxplot Berdasarkan Kota

ggplot(
  data,
  aes(
    x = reorder(wilayah, rata_rata, FUN = median),
    y = rata_rata
  )
) +
  geom_boxplot(aes(group = wilayah), fill = "lightblue", outlier.alpha = 0.4) +
  labs(
    title = "Distribusi Volume Sampah Berdasarkan Kota Administrasi",
    x = "Kota Administrasi",
    y = "Rata-rata Volume Sampah"
  ) +
  theme_minimal() +
  theme(axis.text.x = element_text(angle = 35, hjust = 1))

6 Analisis Temporal

6.1 Agregasi Bulanan

data_bulanan <- data |>
  group_by(tanggal) |>
  summarise(
    rata_rata_volume = mean(rata_rata, na.rm = TRUE),
    median_volume = median(rata_rata, na.rm = TRUE),
    minimum = min(rata_rata, na.rm = TRUE),
    maksimum = max(rata_rata, na.rm = TRUE),
    .groups = "drop"
  )

6.2 Time Series

ggplot(data_bulanan, aes(x = tanggal, y = rata_rata_volume)) +
  geom_line(linewidth = 1) +
  geom_point(size = 1.8) +
  labs(
    title = "Perubahan Rata-Rata Volume Sampah Sungai",
    x = "Periode",
    y = "Rata-rata Volume Sampah"
  ) +
  scale_x_date(date_breaks = "3 months", date_labels = "%b\n%Y") +
  theme_minimal()

6.3 Tren Berdasarkan Kota

data_kota_bulanan <- data |>
  group_by(wilayah, tanggal) |>
  summarise(
    rata_rata_volume = mean(rata_rata, na.rm = TRUE),
    .groups = "drop"
  )

ggplot(
  data_kota_bulanan,
  aes(x = tanggal, y = rata_rata_volume, group = wilayah)
) +
  geom_line(linewidth = 0.9) +
  geom_point(size = 1.3) +
  facet_wrap(~ wilayah, scales = "free_y") +
  labs(
    title = "Pola Temporal Volume Sampah Berdasarkan Kota Administrasi",
    x = "Periode",
    y = "Rata-rata Volume Sampah"
  ) +
  scale_x_date(date_breaks = "6 months", date_labels = "%b\n%Y") +
  theme_minimal()

7 Analisis Outlier

7.1 Batas IQR

Outlier ditentukan menggunakan metode Interquartile Range (IQR).

Batas bawah:

\[ Q1 - 1.5(IQR) \]

Batas atas:

\[ Q3 + 1.5(IQR) \]

Q1 <- quantile(data$rata_rata, 0.25, na.rm = TRUE)
Q3 <- quantile(data$rata_rata, 0.75, na.rm = TRUE)
IQR_value <- IQR(data$rata_rata, na.rm = TRUE)

batas_bawah <- Q1 - 1.5 * IQR_value
batas_atas <- Q3 + 1.5 * IQR_value

tibble(
  Q1 = Q1,
  Q3 = Q3,
  IQR = IQR_value,
  Batas_Bawah = batas_bawah,
  Batas_Atas = batas_atas
) |>
  mutate(across(where(is.numeric), ~ round(.x, 2))) |>
  kable(caption = "Batas Outlier Berdasarkan Metode IQR") |>
  kable_styling(full_width = FALSE)
Batas Outlier Berdasarkan Metode IQR
Q1 Q3 IQR Batas_Bawah Batas_Atas
15.6 29.55 13.95 -5.32 50.46

7.2 Identifikasi Outlier

data_outlier <- data |>
  mutate(
    status_outlier = case_when(
      rata_rata < batas_bawah ~ "Outlier bawah",
      rata_rata > batas_atas ~ "Outlier atas",
      TRUE ~ "Normal"
    )
  )

data_outlier |>
  count(status_outlier) |>
  kable(
    col.names = c("Status", "Jumlah"),
    caption = "Jumlah Observasi Berdasarkan Status Outlier"
  ) |>
  kable_styling(full_width = FALSE)
Jumlah Observasi Berdasarkan Status Outlier
Status Jumlah
Normal 1170
Outlier atas 132

7.3 Detail Outlier

data_outlier |>
  filter(status_outlier != "Normal") |>
  select(periode_data, tanggal, wilayah, kecamatan, rata_rata, status_outlier) |>
  arrange(desc(rata_rata)) |>
  head(20) |>
  mutate(rata_rata = round(rata_rata, 2)) |>
  kable(
    col.names = c("Periode", "Tanggal", "Kota Administrasi",
                  "Kecamatan", "Rata-rata", "Status"),
    caption = "20 Observasi Outlier dengan Nilai Tertinggi"
  ) |>
  kable_styling(
    full_width = FALSE,
    bootstrap_options = c("striped", "hover")
  )
20 Observasi Outlier dengan Nilai Tertinggi
Periode Tanggal Kota Administrasi Kecamatan Rata-rata Status
202511 2025-11-01 KOTA ADM. JAKARTA SELATAN JAGAKARSA 316.87 Outlier atas
202602 2026-02-01 KOTA ADM. JAKARTA TIMUR CAKUNG 242.89 Outlier atas
202602 2026-02-01 KOTA ADM. JAKARTA UTARA PENJARINGAN 239.00 Outlier atas
202602 2026-02-01 KOTA ADM. JAKARTA BARAT CENGKARENG 233.86 Outlier atas
202402 2024-02-01 KOTA ADM. JAKARTA SELATAN JAGAKARSA 220.00 Outlier atas
202602 2026-02-01 KOTA ADM. JAKARTA BARAT KEMBANGAN 219.14 Outlier atas
202602 2026-02-01 KOTA ADM. JAKARTA UTARA TANJUNG PRIOK 216.89 Outlier atas
202602 2026-02-01 KOTA ADM. JAKARTA SELATAN JAGAKARSA 214.75 Outlier atas
202602 2026-02-01 KOTA ADM. JAKARTA TIMUR KRAMATJATI 209.32 Outlier atas
202602 2026-02-01 KOTA ADM. JAKARTA PUSAT KEMAYORAN 208.11 Outlier atas
202602 2026-02-01 KOTA ADM. JAKARTA PUSAT TANAH ABANG 207.29 Outlier atas
202602 2026-02-01 KOTA ADM. JAKARTA BARAT GROGOL PETAMBURAN 206.04 Outlier atas
202602 2026-02-01 KOTA ADM. JAKARTA PUSAT JOHAR BARU 204.96 Outlier atas
202602 2026-02-01 KOTA ADM. JAKARTA BARAT KEBON JERUK 204.75 Outlier atas
202602 2026-02-01 KOTA ADM. JAKARTA TIMUR PASAR REBO 204.75 Outlier atas
202602 2026-02-01 KOTA ADM. JAKARTA BARAT KALIDERES 203.82 Outlier atas
202602 2026-02-01 KOTA ADM. JAKARTA UTARA PADEMANGAN 203.25 Outlier atas
202602 2026-02-01 KOTA ADM. JAKARTA UTARA CILINCING 199.25 Outlier atas
202602 2026-02-01 KOTA ADM. JAKARTA BARAT TAMBORA 198.34 Outlier atas
202602 2026-02-01 KOTA ADM. JAKARTA PUSAT SENEN 197.96 Outlier atas

7.4 Visualisasi Outlier

ggplot(data_outlier, aes(x = tanggal, y = rata_rata, color = status_outlier)) +
  geom_point(alpha = 0.65) +
  geom_hline(yintercept = batas_atas, linetype = "dashed", linewidth = 0.8) +
  labs(
    title = "Identifikasi Outlier Volume Sampah Sungai",
    x = "Periode",
    y = "Rata-rata Volume Sampah",
    color = "Status"
  ) +
  theme_minimal()

Outlier tidak langsung dianggap sebagai kesalahan data. Nilai ekstrem dapat merepresentasikan kondisi lingkungan yang berbeda pada waktu atau lokasi tertentu.

8 Autokorelasi Temporal

Autokorelasi temporal digunakan untuk mengetahui apakah nilai pada suatu periode memiliki hubungan dengan periode sebelumnya.

8.1 ACF

acf(
  data_bulanan$rata_rata_volume,
  main = "Autocorrelation Function Rata-Rata Volume Sampah"
)

Jika spike ACF melewati batas signifikansi, terdapat indikasi hubungan temporal pada lag tersebut.

8.2 Nilai ACF

acf_result <- acf(
  data_bulanan$rata_rata_volume,
  plot = FALSE
)

tibble(
  lag = as.numeric(acf_result$lag),
  autocorrelation = as.numeric(acf_result$acf)
) |>
  filter(lag > 0) |>
  head(12) |>
  mutate(
    lag = round(lag, 0),
    autocorrelation = round(autocorrelation, 3)
  ) |>
  kable(caption = "Nilai Autokorelasi Temporal pada Beberapa Lag") |>
  kable_styling(full_width = FALSE)
Nilai Autokorelasi Temporal pada Beberapa Lag
lag autocorrelation
1 -0.066
2 -0.040
3 -0.050
4 -0.083
5 -0.067
6 -0.007
7 -0.025
8 -0.004
9 -0.015
10 -0.012
11 -0.013
12 -0.018

9 Analisis Geospasial

Data spasial yang digunakan adalah batas administrasi kecamatan. Unit spasial dibuat sama dengan unit pada data statistik.

9.1 Folder Shapefile

folder_kota <- c(
  "JAKARTA PUSAT",
  "JAKARTA SELATAN",
  "JAKARTA BARAT",
  "JAKARTA TIMUR",
  "JAKARTA UTARA"
)

file_shp <- file.path(
  folder_kota,
  "ADMINISTRASIKECAMATAN_AR_25K.shp"
)

tibble(
  kota = folder_kota,
  file_shp = file_shp,
  tersedia = file.exists(file_shp)
) |>
  kable(caption = "Daftar Shapefile Batas Kecamatan") |>
  kable_styling(full_width = FALSE)
Daftar Shapefile Batas Kecamatan
kota file_shp tersedia
JAKARTA PUSAT JAKARTA PUSAT/ADMINISTRASIKECAMATAN_AR_25K.shp TRUE
JAKARTA SELATAN JAKARTA SELATAN/ADMINISTRASIKECAMATAN_AR_25K.shp TRUE
JAKARTA BARAT JAKARTA BARAT/ADMINISTRASIKECAMATAN_AR_25K.shp TRUE
JAKARTA TIMUR JAKARTA TIMUR/ADMINISTRASIKECAMATAN_AR_25K.shp TRUE
JAKARTA UTARA JAKARTA UTARA/ADMINISTRASIKECAMATAN_AR_25K.shp TRUE

9.2 Membaca Seluruh Shapefile

shp_list <- map2(
  file_shp,
  folder_kota,
  ~ st_read(.x, quiet = TRUE) |>
    st_make_valid() |>
    st_transform(4326) |>
    mutate(kota_shp = .y)
)

shp_jakarta <- bind_rows(shp_list)

9.3 Field Shapefile

tibble(
  nomor = seq_along(names(shp_jakarta)),
  field = names(shp_jakarta)
) |>
  kable(caption = "Field pada Shapefile Administrasi Kecamatan") |>
  kable_styling(full_width = FALSE)
Field pada Shapefile Administrasi Kecamatan
nomor field
1 KDPPUM
2 NAMOBJ
3 REMARK
4 KDPBPS
5 FCODE
6 LUASWH
7 UUPP
8 SRS_ID
9 LCODE
10 METADATA
11 KDEBPS
12 KDEPUM
13 KDCBPS
14 KDCPUM
15 KDBBPS
16 KDBPUM
17 WADMKD
18 WIADKD
19 WADMKC
20 WIADKC
21 WADMKK
22 WIADKK
23 WADMPR
24 WIADPR
25 TIPADM
26 SHAPE_Leng
27 SHAPE_Area
28 kota_shp
29 geometry

9.4 Deteksi Field Nama Kecamatan

kecamatan_data <- data |>
  mutate(kecamatan_clean = str_to_upper(str_squish(kecamatan))) |>
  distinct(kecamatan_clean) |>
  pull(kecamatan_clean)

candidate_fields <- names(shp_jakarta)[
  !names(shp_jakarta) %in% c("geometry", "kota_shp")
]

field_score <- map_dfr(
  candidate_fields,
  function(f) {
    nilai_shp <- shp_jakarta[[f]]

    if (!is.character(nilai_shp) && !is.factor(nilai_shp)) {
      return(tibble(field = f, matched = 0, total_unique = 0))
    }

    nilai_shp <- str_to_upper(str_squish(as.character(nilai_shp)))

    tibble(
      field = f,
      matched = sum(nilai_shp %in% kecamatan_data, na.rm = TRUE),
      total_unique = n_distinct(nilai_shp)
    )
  }
) |>
  arrange(desc(matched), desc(total_unique))

field_score |>
  head(15) |>
  kable(
    caption = "Skor Kecocokan Field Shapefile dengan Nama Kecamatan"
  ) |>
  kable_styling(full_width = FALSE)
Skor Kecocokan Field Shapefile dengan Nama Kecamatan
field matched total_unique
NAMOBJ 41 42
WADMKK 0 5
KDPPUM 0 1
REMARK 0 1
KDPBPS 0 1
FCODE 0 1
UUPP 0 1
SRS_ID 0 1
LCODE 0 1
METADATA 0 1
KDEBPS 0 1
KDEPUM 0 1
KDCBPS 0 1
KDCPUM 0 1
KDBBPS 0 1
field_kecamatan <- field_score |>
  filter(matched == max(matched, na.rm = TRUE)) |>
  slice(1) |>
  pull(field)

field_kecamatan
## [1] "NAMOBJ"

9.5 Standardisasi Nama Kecamatan

shp_jakarta <- shp_jakarta |>
  mutate(
    kecamatan = str_to_upper(
      str_squish(as.character(.data[[field_kecamatan]]))
    )
  )

data_spasial <- data |>
  mutate(
    kecamatan = str_to_upper(str_squish(kecamatan))
  )

9.6 Pemeriksaan Kecocokan

shp_jakarta |>
  st_drop_geometry() |>
  distinct(kecamatan, kota_shp) |>
  mutate(cocok = kecamatan %in% data_spasial$kecamatan) |>
  count(cocok) |>
  kable(
    col.names = c("Nama Kecamatan Cocok", "Jumlah"),
    caption = "Pemeriksaan Kecocokan Nama Kecamatan"
  ) |>
  kable_styling(full_width = FALSE)
Pemeriksaan Kecocokan Nama Kecamatan
Nama Kecamatan Cocok Jumlah
FALSE 1
TRUE 41

10 Peta Administrasi

ggplot(shp_jakarta) +
  geom_sf(fill = "grey95", color = "grey40", linewidth = 0.25) +
  labs(
    title = "Peta Administrasi Kecamatan DKI Jakarta",
    subtitle = "Batas wilayah berdasarkan shapefile administrasi kecamatan",
    x = NULL, y = NULL
  ) +
  theme_minimal()

11 Penggabungan Data Statistik dan Spasial

Data statistik diringkas pada tingkat kecamatan selama seluruh periode pengamatan.

data_spasial_kecamatan <- data_spasial |>
  group_by(wilayah, kecamatan) |>
  summarise(
    rata_rata = mean(rata_rata, na.rm = TRUE),
    median = median(rata_rata, na.rm = TRUE),
    sd = sd(rata_rata, na.rm = TRUE),
    minimum = min(rata_rata, na.rm = TRUE),
    maksimum = max(rata_rata, na.rm = TRUE),
    jumlah_observasi = n(),
    .groups = "drop"
  ) |>
  mutate(
    kota_shp = case_when(
      wilayah == "KOTA ADM. JAKARTA PUSAT" ~ "JAKARTA PUSAT",
      wilayah == "KOTA ADM. JAKARTA SELATAN" ~ "JAKARTA SELATAN",
      wilayah == "KOTA ADM. JAKARTA BARAT" ~ "JAKARTA BARAT",
      wilayah == "KOTA ADM. JAKARTA TIMUR" ~ "JAKARTA TIMUR",
      wilayah == "KOTA ADM. JAKARTA UTARA" ~ "JAKARTA UTARA",
      TRUE ~ NA_character_
    )
  )

jakarta_map <- shp_jakarta |>
  left_join(
    data_spasial_kecamatan,
    by = c("kecamatan", "kota_shp")
  )

11.1 Pemeriksaan Join

jakarta_map |>
  st_drop_geometry() |>
  summarise(
    total_polygon = n(),
    polygon_dengan_data = sum(!is.na(rata_rata)),
    polygon_tanpa_data = sum(is.na(rata_rata))
  ) |>
  kable(caption = "Pemeriksaan Penggabungan Data Statistik dan Spasial") |>
  kable_styling(full_width = FALSE)
Pemeriksaan Penggabungan Data Statistik dan Spasial
total_polygon polygon_dengan_data polygon_tanpa_data
42 41 1

12 Peta Tematik

12.1 Rata-Rata Volume Seluruh Periode

ggplot(
  jakarta_map |> filter(!is.na(rata_rata)),
  aes(fill = rata_rata)
) +
  geom_sf(color = "white", linewidth = 0.25) +
  scale_fill_viridis_c(
    option = "C",
    name = "Rata-rata\nvolume"
  ) +
  facet_wrap(~ kota_shp) +
  labs(
    title = "Distribusi Spasial Rata-Rata Volume Sampah Sungai",
    subtitle = "Rata-rata seluruh periode pada tingkat kecamatan",
    x = NULL, y = NULL
  ) +
  theme_minimal() +
  theme(
    axis.text = element_blank(),
    panel.grid = element_blank(),
    strip.text = element_text(face = "bold")
  )

Peta menunjukkan variasi volume sampah antar kecamatan. Kecamatan dengan nilai rata-rata lebih tinggi menunjukkan volume sampah yang relatif lebih besar selama periode pengamatan.

12.2 Ranking Kecamatan

data_spasial_kecamatan |>
  arrange(desc(rata_rata)) |>
  mutate(peringkat = row_number()) |>
  select(peringkat, wilayah, kecamatan, rata_rata, median, maksimum) |>
  head(15) |>
  mutate(across(where(is.numeric), ~ round(.x, 2))) |>
  kable(
    col.names = c("Peringkat", "Kota Administrasi", "Kecamatan",
                  "Mean", "Median", "Maksimum"),
    caption = "15 Kecamatan dengan Rata-Rata Volume Sampah Tertinggi"
  ) |>
  kable_styling(
    full_width = FALSE,
    bootstrap_options = c("striped", "hover")
  )
15 Kecamatan dengan Rata-Rata Volume Sampah Tertinggi
Peringkat Kota Administrasi Kecamatan Mean Median Maksimum
1 KOTA ADM. JAKARTA SELATAN JAGAKARSA 83.37 83.37 83.37
2 KOTA ADM. JAKARTA UTARA PENJARINGAN 63.20 63.20 63.20
3 KOTA ADM. JAKARTA TIMUR CAKUNG 58.67 58.67 58.67
4 KOTA ADM. JAKARTA BARAT CENGKARENG 57.86 57.86 57.86
5 KOTA ADM. JAKARTA BARAT KEMBANGAN 46.52 46.52 46.52
6 KOTA ADM. JAKARTA UTARA TANJUNG PRIOK 44.30 44.30 44.30
7 KOTA ADM. JAKARTA BARAT KEBON JERUK 42.56 42.56 42.56
8 KOTA ADM. JAKARTA TIMUR KRAMATJATI 39.21 39.21 39.21
9 KOTA ADM. JAKARTA BARAT GROGOL PETAMBURAN 34.71 34.71 34.71
10 KOTA ADM. JAKARTA BARAT KALIDERES 34.62 34.62 34.62
11 KOTA ADM. JAKARTA BARAT TAMBORA 34.44 34.44 34.44
12 KOTA ADM. JAKARTA PUSAT KEMAYORAN 33.83 33.83 33.83
13 KOTA ADM. JAKARTA TIMUR PULOGADUNG 33.14 33.14 33.14
14 KOTA ADM. JAKARTA BARAT PAL MERAH 31.94 31.94 31.94
15 KOTA ADM. JAKARTA UTARA CILINCING 31.73 31.73 31.73

13 Peta Periode Terakhir

periode_terakhir <- max(data$tanggal, na.rm = TRUE)

data_latest <- data |>
  filter(tanggal == periode_terakhir) |>
  group_by(wilayah, kecamatan) |>
  summarise(
    rata_rata = mean(rata_rata, na.rm = TRUE),
    .groups = "drop"
  ) |>
  mutate(
    kota_shp = case_when(
      wilayah == "KOTA ADM. JAKARTA PUSAT" ~ "JAKARTA PUSAT",
      wilayah == "KOTA ADM. JAKARTA SELATAN" ~ "JAKARTA SELATAN",
      wilayah == "KOTA ADM. JAKARTA BARAT" ~ "JAKARTA BARAT",
      wilayah == "KOTA ADM. JAKARTA TIMUR" ~ "JAKARTA TIMUR",
      wilayah == "KOTA ADM. JAKARTA UTARA" ~ "JAKARTA UTARA",
      TRUE ~ NA_character_
    )
  )

map_latest <- shp_jakarta |>
  left_join(data_latest, by = c("kecamatan", "kota_shp"))
ggplot(
  map_latest |> filter(!is.na(rata_rata)),
  aes(fill = rata_rata)
) +
  geom_sf(color = "white", linewidth = 0.25) +
  scale_fill_viridis_c(option = "C", name = "Volume") +
  facet_wrap(~ kota_shp) +
  labs(
    title = "Peta Volume Sampah Sungai pada Periode Terakhir",
    subtitle = format(periode_terakhir, "%B %Y"),
    x = NULL, y = NULL
  ) +
  theme_minimal() +
  theme(
    axis.text = element_blank(),
    panel.grid = element_blank(),
    strip.text = element_text(face = "bold")
  )

14 Peta Frekuensi Outlier

outlier_frequency <- data_outlier |>
  group_by(wilayah, kecamatan) |>
  summarise(
    jumlah_outlier = sum(status_outlier != "Normal", na.rm = TRUE),
    .groups = "drop"
  ) |>
  mutate(
    kota_shp = case_when(
      wilayah == "KOTA ADM. JAKARTA PUSAT" ~ "JAKARTA PUSAT",
      wilayah == "KOTA ADM. JAKARTA SELATAN" ~ "JAKARTA SELATAN",
      wilayah == "KOTA ADM. JAKARTA BARAT" ~ "JAKARTA BARAT",
      wilayah == "KOTA ADM. JAKARTA TIMUR" ~ "JAKARTA TIMUR",
      wilayah == "KOTA ADM. JAKARTA UTARA" ~ "JAKARTA UTARA",
      TRUE ~ NA_character_
    )
  )

map_outlier <- shp_jakarta |>
  left_join(outlier_frequency, by = c("kecamatan", "kota_shp"))
ggplot(
  map_outlier |> filter(!is.na(jumlah_outlier)),
  aes(fill = jumlah_outlier)
) +
  geom_sf(color = "white", linewidth = 0.25) +
  scale_fill_viridis_c(option = "B", name = "Jumlah\noutlier") +
  facet_wrap(~ kota_shp) +
  labs(
    title = "Distribusi Spasial Frekuensi Outlier",
    subtitle = "Jumlah observasi outlier pada setiap kecamatan",
    x = NULL, y = NULL
  ) +
  theme_minimal() +
  theme(
    axis.text = element_blank(),
    panel.grid = element_blank(),
    strip.text = element_text(face = "bold")
  )

15 Global Moran’s I

Moran’s I digunakan untuk mengetahui apakah kecamatan yang berdekatan memiliki nilai volume sampah yang cenderung serupa.

15.1 Menyiapkan Unit Spasial

moran_map <- jakarta_map |>
  filter(!is.na(rata_rata), !is.na(kota_shp)) |>
  arrange(kota_shp, kecamatan)

15.2 Spatial Neighbors

Queen contiguity digunakan untuk menentukan hubungan antarwilayah. Dua kecamatan dianggap bertetangga apabila memiliki bagian batas atau titik yang bersinggungan.

nb_jakarta <- poly2nb(
  moran_map,
  queen = TRUE
)

lw_jakarta <- nb2listw(
  nb_jakarta,
  style = "W",
  zero.policy = TRUE
)

15.3 Jumlah Tetangga

tibble(
  kecamatan = moran_map$kecamatan,
  jumlah_tetangga = card(nb_jakarta)
) |>
  arrange(desc(jumlah_tetangga)) |>
  kable(caption = "Jumlah Tetangga Setiap Kecamatan") |>
  kable_styling(full_width = FALSE)
Jumlah Tetangga Setiap Kecamatan
kecamatan jumlah_tetangga
GAMBIR 8
KEMAYORAN 8
KRAMATJATI 8
SENEN 7
JATINEGARA 7
PULO GADUNG 7
GROGOL PETAMBURAN 6
KEBON JERUK 6
KEBAYORAN LAMA 6
PASAR MINGGU 6
TEBET 6
MATRAMAN 6
KELAPA GADING 6
PADEMANGAN 6
PENJARINGAN 6
CENGKARENG 5
PALMERAH 5
TAMAN SARI 5
TAMBORA 5
CEMPAKA PUTIH 5
MENTENG 5
SAWAH BESAR 5
TANAH ABANG 5
CILANDAK 5
MAMPANG PRAPATAN 5
PANCORAN 5
KEBAYORAN BARU 4
CAKUNG 4
DUREN SAWIT 4
MAKASAR 4
PASAR REBO 4
TANJUNG PRIOK 4
KEMBANGAN 3
JOHAR BARU 3
JAGAKARSA 3
PESANGGRAHAN 3
CIPAYUNG 3
CIRACAS 3
CILINCING 3
KOJA 3
KALIDERES 2

15.4 Uji Moran’s I

moran_result <- moran.test(
  moran_map$rata_rata,
  lw_jakarta,
  zero.policy = TRUE
)

moran_result
## 
##  Moran I test under randomisation
## 
## data:  moran_map$rata_rata  
## weights: lw_jakarta    
## 
## Moran I statistic standard deviate = 1.7765, p-value = 0.03783
## alternative hypothesis: greater
## sample estimates:
## Moran I statistic       Expectation          Variance 
##       0.135438804      -0.025000000       0.008156469

15.5 Ringkasan Moran’s I

tibble(
  Moran_I = as.numeric(moran_result$estimate[1]),
  Expected_I = as.numeric(moran_result$estimate[2]),
  Variance = as.numeric(moran_result$estimate[3]),
  P_value = moran_result$p.value
) |>
  mutate(across(where(is.numeric), ~ round(.x, 4))) |>
  kable(caption = "Ringkasan Global Moran's I") |>
  kable_styling(full_width = FALSE)
Ringkasan Global Moran’s I
Moran_I Expected_I Variance P_value
0.1354 -0.025 0.0082 0.0378

Jika Moran’s I positif dan signifikan, terdapat indikasi pengelompokan spasial nilai yang serupa. Jika tidak signifikan, belum terdapat bukti statistik yang cukup untuk menyatakan adanya autokorelasi spasial global.

16 Local Moran’s I

Local Moran’s I digunakan untuk mengetahui lokasi spesifik yang membentuk pengelompokan spasial.

local_moran <- localmoran(
  moran_map$rata_rata,
  lw_jakarta,
  zero.policy = TRUE
)

moran_map <- moran_map |>
  mutate(
    local_I = local_moran[, "Ii"],
    local_z = local_moran[, "Z.Ii"],
    local_p = local_moran[, "Pr(z != E(Ii))"]
  )

16.1 Klasifikasi Cluster

nilai_rata2 <- mean(moran_map$rata_rata, na.rm = TRUE)

moran_map <- moran_map |>
  mutate(
    lag_rata_rata = lag.listw(
      lw_jakarta,
      rata_rata,
      zero.policy = TRUE
    ),
    cluster = case_when(
      rata_rata >= nilai_rata2 &
        lag_rata_rata >= nilai_rata2 &
        local_p < 0.05 ~ "High-High",

      rata_rata < nilai_rata2 &
        lag_rata_rata < nilai_rata2 &
        local_p < 0.05 ~ "Low-Low",

      rata_rata >= nilai_rata2 &
        lag_rata_rata < nilai_rata2 &
        local_p < 0.05 ~ "High-Low",

      rata_rata < nilai_rata2 &
        lag_rata_rata >= nilai_rata2 &
        local_p < 0.05 ~ "Low-High",

      TRUE ~ "Tidak signifikan"
    )
  )

16.2 Ringkasan Cluster

moran_map |>
  st_drop_geometry() |>
  count(cluster) |>
  arrange(desc(n)) |>
  kable(
    col.names = c("Kategori Cluster", "Jumlah Kecamatan"),
    caption = "Klasifikasi Local Spatial Autocorrelation"
  ) |>
  kable_styling(full_width = FALSE)
Klasifikasi Local Spatial Autocorrelation
Kategori Cluster Jumlah Kecamatan
Tidak signifikan 37
High-High 3
High-Low 1

17 Peta Local Moran’s I

ggplot(moran_map, aes(fill = cluster)) +
  geom_sf(color = "white", linewidth = 0.3) +
  labs(
    title = "Local Moran's I Volume Sampah Sungai",
    subtitle = "Identifikasi pola pengelompokan spasial",
    fill = "Cluster",
    x = NULL, y = NULL
  ) +
  facet_wrap(~ kota_shp) +
  theme_minimal() +
  theme(
    axis.text = element_blank(),
    panel.grid = element_blank(),
    strip.text = element_text(face = "bold")
  )

Pola High-High menunjukkan kecamatan dengan volume relatif tinggi yang dikelilingi kecamatan dengan volume relatif tinggi. Pola Low-Low menunjukkan kecamatan dengan volume relatif rendah yang berdekatan dengan kecamatan bernilai rendah. High-Low dan Low-High menunjukkan ketidaksesuaian antara suatu kecamatan dan wilayah di sekitarnya.

18 Peta Nilai Local Moran’s I

ggplot(moran_map, aes(fill = local_I)) +
  geom_sf(color = "white", linewidth = 0.3) +
  scale_fill_viridis_c(
    option = "C",
    name = "Local Moran's I"
  ) +
  labs(
    title = "Distribusi Local Moran's I",
    subtitle = "Kekuatan autokorelasi spasial lokal",
    x = NULL, y = NULL
  ) +
  facet_wrap(~ kota_shp) +
  theme_minimal() +
  theme(
    axis.text = element_blank(),
    panel.grid = element_blank(),
    strip.text = element_text(face = "bold")
  )

19 Kecamatan dengan Volume Tertinggi

data_spasial_kecamatan |>
  arrange(desc(rata_rata)) |>
  slice_head(n = 10) |>
  mutate(rata_rata = round(rata_rata, 2)) |>
  select(wilayah, kecamatan, rata_rata) |>
  kable(
    col.names = c("Kota Administrasi", "Kecamatan", "Rata-rata Volume"),
    caption = "10 Kecamatan dengan Rata-Rata Volume Sampah Tertinggi"
  ) |>
  kable_styling(
    full_width = FALSE,
    bootstrap_options = c("striped", "hover")
  )
10 Kecamatan dengan Rata-Rata Volume Sampah Tertinggi
Kota Administrasi Kecamatan Rata-rata Volume
KOTA ADM. JAKARTA SELATAN JAGAKARSA 83.37
KOTA ADM. JAKARTA UTARA PENJARINGAN 63.20
KOTA ADM. JAKARTA TIMUR CAKUNG 58.67
KOTA ADM. JAKARTA BARAT CENGKARENG 57.86
KOTA ADM. JAKARTA BARAT KEMBANGAN 46.52
KOTA ADM. JAKARTA UTARA TANJUNG PRIOK 44.30
KOTA ADM. JAKARTA BARAT KEBON JERUK 42.56
KOTA ADM. JAKARTA TIMUR KRAMATJATI 39.21
KOTA ADM. JAKARTA BARAT GROGOL PETAMBURAN 34.71
KOTA ADM. JAKARTA BARAT KALIDERES 34.62

20 Kecamatan dengan Volume Terendah

data_spasial_kecamatan |>
  arrange(rata_rata) |>
  slice_head(n = 10) |>
  mutate(rata_rata = round(rata_rata, 2)) |>
  select(wilayah, kecamatan, rata_rata) |>
  kable(
    col.names = c("Kota Administrasi", "Kecamatan", "Rata-rata Volume"),
    caption = "10 Kecamatan dengan Rata-Rata Volume Sampah Terendah"
  ) |>
  kable_styling(
    full_width = FALSE,
    bootstrap_options = c("striped", "hover")
  )
10 Kecamatan dengan Rata-Rata Volume Sampah Terendah
Kota Administrasi Kecamatan Rata-rata Volume
KOTA ADM. JAKARTA SELATAN PANCORAN 15.74
KOTA ADM. JAKARTA TIMUR CIPAYUNG 16.87
KOTA ADM. JAKARTA PUSAT CEMPAKA PUTIH 17.91
KOTA ADM. JAKARTA TIMUR CIRACAS 18.64
KOTA ADM. JAKARTA TIMUR MATRAMAN 19.93
KOTA ADM. JAKARTA SELATAN SETIABUDI 20.38
KOTA ADM. JAKARTA SELATAN KEBAYORAN LAMA 21.37
KOTA ADM. JAKARTA PUSAT SENEN 21.43
KOTA ADM. JAKARTA TIMUR JATINEGARA 21.44
KOTA ADM. JAKARTA SELATAN PASAR MINGGU 21.51

21 Ringkasan Hasil

tibble(
  indikator = c(
    "Jumlah observasi",
    "Jumlah kecamatan",
    "Jumlah kota administrasi",
    "Periode awal",
    "Periode akhir",
    "Mean",
    "Median",
    "Standar deviasi",
    "Minimum",
    "Maksimum",
    "IQR",
    "Jumlah outlier"
  ),
  nilai = c(
    nrow(data),
    n_distinct(data$kecamatan),
    n_distinct(data$wilayah),
    format(min(data$tanggal), "%B %Y"),
    format(max(data$tanggal), "%B %Y"),
    round(mean(data$rata_rata, na.rm = TRUE), 2),
    round(median(data$rata_rata, na.rm = TRUE), 2),
    round(sd(data$rata_rata, na.rm = TRUE), 2),
    round(min(data$rata_rata, na.rm = TRUE), 2),
    round(max(data$rata_rata, na.rm = TRUE), 2),
    round(IQR(data$rata_rata, na.rm = TRUE), 2),
    sum(data_outlier$status_outlier != "Normal")
  )
) |>
  kable(
    col.names = c("Indikator", "Nilai"),
    caption = "Ringkasan Hasil Eksplorasi Data"
  ) |>
  kable_styling(full_width = FALSE)
Ringkasan Hasil Eksplorasi Data
Indikator Nilai
Jumlah observasi 1302
Jumlah kecamatan 46
Jumlah kota administrasi 5
Periode awal January 2024
Periode akhir July 2026
Mean 30.38
Median 20.71
Standar deviasi 35.82
Minimum 0.47
Maksimum 316.87
IQR 13.95
Jumlah outlier 132

22 Kesimpulan

Berdasarkan hasil eksplorasi, volume sampah sungai di DKI Jakarta memiliki variasi antarobservasi, antarperiode, dan antarwilayah. Statistik deskriptif memberikan gambaran mengenai kecenderungan pusat dan penyebaran data, sedangkan histogram dan boxplot memperlihatkan distribusi serta observasi ekstrem.

Analisis time series digunakan untuk melihat perubahan volume sampah dari waktu ke waktu, sedangkan ACF digunakan untuk mengevaluasi ketergantungan antarperiode.

Pada aspek spasial, peta tematik memperlihatkan variasi volume sampah antar kecamatan. Global Moran’s I digunakan untuk menguji autokorelasi spasial secara keseluruhan, sedangkan Local Moran’s I digunakan untuk mengidentifikasi pola pengelompokan lokal seperti High-High, Low-Low, High-Low, dan Low-High.

Secara keseluruhan, kombinasi analisis statistik, temporal, dan geospasial memberikan gambaran yang lebih lengkap mengenai karakteristik volume sampah sungai di DKI Jakarta.