knitr::opts_chunk$set(warning = FALSE, message = FALSE)Praktikum 3 : Eksplorasi Data Areal
Review Data Areal/Lattice
Data areal (lattice data) merupakan bentuk data spasial yang bersifat diskrit karena merepresentasikan informasi dalam batas wilayah tertentu. Data ini muncul ketika suatu fenomena atau jumlah kejadian digabungkan ke dalam area administratif, misalnya jumlah kasus penyakit dalam epidemiologi spasial atau jumlah kelahiran pada suatu kabupaten. Dengan pendekatan ini, peneliti dapat mengidentifikasi pola geografis, memahami distribusi fenomena, dan menelusuri faktor risiko yang mungkin berhubungan dengan kondisi lingkungan maupun sosial.
Dalam praktik analisis, data areal umumnya tersedia dalam bentuk objek vektor dengan format shapefile. Format ini menjadi standar paling umum untuk menyimpan data spasial karena mampu memuat informasi geometri (seperti poligon batas wilayah) sekaligus atribut nonspasial yang menyertainya. Di R, data spasial tersebut dapat dikelola menggunakan paket sf yang dirancang khusus untuk memudahkan manipulasi dan analisis data berbasis spasial. Sementara itu, data nonspasial biasanya disimpan dalam format umum seperti file CSV atau .xlsx. Dengan kombinasi kedua jenis data ini, analisis spasial dapat dilakukan secara lebih komprehensif, baik dari sisi geografis maupun dari sisi atribut yang melekat pada setiap area.
library(sf)
library(sp)
library(mapview)
library(spData)
library(ggplot2)
library(spatstat)
library(tidyverse)
library(spData)
library(readxl)list.files(system.file("shapes", package = "spData")) [1] "auckland.gpkg" "boston_tracts.gpkg" "columbus.gpkg"
[4] "cycle_hire_osm.geojson" "cycle_hire.geojson" "eire.gpkg"
[7] "NY8_bna_utm18.gpkg" "NY8_utm18.gpkg" "sids.gpkg"
[10] "wheat.gpkg" "world.gpkg"
d2 <- st_read(system.file("shapes/columbus.gpkg",
package = "spData"), quiet = TRUE)
st_crs(d2) <- NAnames(d2) [1] "AREA" "PERIMETER" "COLUMBUS_" "COLUMBUS_I" "POLYID"
[6] "NEIG" "HOVAL" "INC" "CRIME" "OPEN"
[11] "PLUMB" "DISCBD" "X" "Y" "NSA"
[16] "NSB" "EW" "CP" "THOUS" "NEIGNO"
[21] "geom"
print(d2)Simple feature collection with 49 features and 20 fields
Geometry type: POLYGON
Dimension: XY
Bounding box: xmin: 5.874907 ymin: 10.78863 xmax: 11.28742 ymax: 14.74245
CRS: NA
First 10 features:
AREA PERIMETER COLUMBUS_ COLUMBUS_I POLYID NEIG HOVAL INC CRIME
1 0.309441 2.440629 2 5 1 5 80.467 19.531 15.725980
2 0.259329 2.236939 3 1 2 1 44.567 21.232 18.801754
3 0.192468 2.187547 4 6 3 6 26.350 15.956 30.626781
4 0.083841 1.427635 5 2 4 2 33.200 4.477 32.387760
5 0.488888 2.997133 6 7 5 7 23.225 11.252 50.731510
6 0.283079 2.335634 7 8 6 8 28.750 16.029 26.066658
7 0.257084 2.554577 8 4 7 4 75.000 8.438 0.178269
8 0.204954 2.139524 9 3 8 3 37.125 11.337 38.425858
9 0.500755 3.169707 10 18 9 18 52.600 17.586 30.515917
10 0.246689 2.087235 11 10 10 10 96.400 13.598 34.000835
OPEN PLUMB DISCBD X Y NSA NSB EW CP THOUS NEIGNO
1 2.850747 0.217155 5.03 38.80 44.07 1 1 1 0 1000 1005
2 5.296720 0.320581 4.27 35.62 42.38 1 1 0 0 1000 1001
3 4.534649 0.374404 3.89 39.82 41.18 1 1 1 0 1000 1006
4 0.394427 1.186944 3.70 36.50 40.52 1 1 0 0 1000 1002
5 0.405664 0.624596 2.83 40.01 38.00 1 1 1 0 1000 1007
6 0.563075 0.254130 3.78 43.75 39.28 1 1 1 0 1000 1008
7 0.000000 2.402402 2.74 33.36 38.41 1 1 0 0 1000 1004
8 3.483478 2.739726 2.89 36.71 38.71 1 1 0 0 1000 1003
9 0.527488 0.890736 3.17 43.44 35.92 1 1 1 0 1000 1018
10 1.548348 0.557724 4.33 47.61 36.42 1 1 1 0 1000 1010
geom
1 POLYGON ((8.624129 14.23698...
2 POLYGON ((8.25279 14.23694,...
3 POLYGON ((8.653305 14.00809...
4 POLYGON ((8.459499 13.82035...
5 POLYGON ((8.685274 13.63952...
6 POLYGON ((9.401384 13.5504,...
7 POLYGON ((8.037741 13.60752...
8 POLYGON ((8.247527 13.58651...
9 POLYGON ((9.333297 13.27242...
10 POLYGON ((10.08251 13.03377...
ggplot(d2) + geom_sf(aes(fill = CRIME)) + theme_minimal()Membuat objek sf
Objek sf (simple feature) di R adalah struktur data yang digunakan untuk menyimpan data spasial vektor. Objek ini menggabungkan data atribut (seperti data frame) dengan geometri spasial.
Data Atribut:
Mirip dengan data frame biasa, bagian ini berisi kolom-kolom yang menyimpan informasi non-spasial (atribut) tentang setiap fitur. Misalnya, nama, ID, populasi, dll.
Kolom Geometri:
Kolom khusus yang menyimpan informasi geometri dari setiap fitur. Kolom ini biasanya dinamai
geometryataugeom, tetapi bisa diberi nama lain. Geometri ini bisa berupa titik, garis, poligon, multipoint, atau multipoligon.Sistem Referensi Koordinat (CRS):
Informasi tentang sistem referensi koordinat yang digunakan untuk geometri. CRS menentukan bagaimana koordinat dipetakan ke permukaan bumi.
Atribut Metadata:
Informasi tambahan tentang objek
sf, seperti nama kolom geometri, CRS, dan tipe geometri.
Kita dapat menggunakan st_sf() fungsi tersebut untuk membuat objek sf dengan menyediakan dua elemen, yaitu data frame dengan atribut setiap fitur dan kolom daftar geometri fitur sederhana (simple feature geometri list-column/sfc) yang berisi geometri fitur sederhana (simple feature geomteries/sfg).
# Membuat data frame dengan atribut
df <- data.frame(id = 1:3,
name = c("A", "B", "C"))
# Membuat geometri (titik)
p1 <- st_point(c(7.35, 52.42))
p2 <- st_point(c(7.22, 52.18))
p3 <- st_point(c(7.44, 52.19))
geom <- st_sfc(list(p1, p2, p3), crs = 'EPSG:4326')
# Menggabungkan data frame dan geometri menjadi objek sf
sf_obj <- st_sf(df, geometry = geom)
# Menampilkan objek sf
print(sf_obj)Simple feature collection with 3 features and 2 fields
Geometry type: POINT
Dimension: XY
Bounding box: xmin: 7.22 ymin: 52.18 xmax: 7.44 ymax: 52.42
Geodetic CRS: WGS 84
id name geometry
1 1 A POINT (7.35 52.42)
2 2 B POINT (7.22 52.18)
3 3 C POINT (7.44 52.19)
POINT Satu titik. Contoh: lokasi sensor atau titik GPS.
MULTIPOINT Sekumpulan titik dalam satu fitur. Contoh: sebaran pohon dalam satu plot.
LINESTRING Garis tunggal yang terdiri atas segmen-segmen. Contoh: satu ruas jalan atau sungai.
MULTILINESTRING Beberapa garis dalam satu fitur. Contoh: jaringan jalan yang dimiliki satu entitas.
POLYGON Satu poligon dengan cangkang luar (boleh ada lubang/holes). Contoh: batas satu kelurahan.
MULTIPOLYGON Sekumpulan poligon dalam satu fitur. Contoh: kabupaten yang terdiri atas beberapa pulau terpisah.
GEOMETRYCOLLECTION Kumpulan berbagai tipe geometri (titik, garis, poligon) dalam satu fitur.
Implementasi Data Area : Data Jawa Barat
Data yang digunakan dalam praktik ini merupakan data sekunder berupa data kependudukan yang diperoleh dari Badan Pusat Statistik (BPS) dan peta shapefile berisi peta Provinsi Jawa Barat. Berdasarkan data tersebut, tahap awal yaitu proses menggabungkan data bentuk geometri wilayah (peta shp Jawa Barat) dengan data statistik atribut dalam format xlsx. Dataset mencakup informasi spasial dan tabular tingkat kabupaten/kota di Provinsi Jawa Barat untuk tahun 2015 dan 2016. Praktik ini melibatkan variabel utama, yaitu %Kemiskinan tahun 2015. Data dapat didownload pada link berikut https://bit.ly/data_praktikum3
setwd("/Users/rositariarusesta/Documents/Regresi Spasial")
shp_jabar <- st_read("petaJabar2/Jabar2.shp")Reading layer `Jabar2' from data source
`/Users/rositariarusesta/Documents/Regresi Spasial/PetaJabar2/Jabar2.shp'
using driver `ESRI Shapefile'
Simple feature collection with 26 features and 7 fields
Geometry type: MULTIPOLYGON
Dimension: XY
Bounding box: xmin: 106.3705 ymin: -7.823398 xmax: 108.8338 ymax: -5.91377
CRS: NA
data_jabar <- read_excel("data_jabar.xlsx")
data_jabar$KABKOTNO <- as.character(data_jabar$KABKOTNO)
# join
data_spasial <- shp_jabar %>%
left_join(data_jabar, by = "KABKOTNO") %>%
na.omit()#Cek semua nama kolom
names(data_spasial) [1] "PROVNO.x" "KABKOTNO" "KODE2010.x" "PROVINSI.x" "KABKOT.x"
[6] "SUMBER" "IDSP2010.x" "PROVNO.y" "KODE2010.y" "PROVINSI.y"
[11] "KABKOT.y" "IDSP2010.y" "Long" "Lat" "p.miskin15"
[16] "p.miskin16" "j.miskin15" "j.miskin16" "AHH2015" "AHH2016"
[21] "EYS2015" "EYS2016" "MYS2015" "MYS2016" "EXP2015"
[26] "EXP2016" "APM.SD15" "APM.SMP15" "APM.SMA15" "APM.PT15"
[31] "APK.SD15" "APK.SMP15" "APK.SMA15" "APK.PT15" "APS.USIA15"
[36] "APS.USIA2" "APS.USIA3" "APS.USIA4" "geometry"
#Memilih data yang mengandung variabel saja
data_filter<-data_spasial[,15:37]#Visualisasi data Jabar
plot(data_filter)library(ggplot2)
plot_kemiskinan <- ggplot(data = data_spasial) +
geom_sf(aes(fill = p.miskin15), color = "white", linewidth = 0.2) +
# Menambahkan label nama kabupaten/kota:
geom_sf_text(aes(label = KABKOT.x), size = 2, color = "black", check_overlap = TRUE) +
scale_fill_viridis_c(option = "magma", name = "Persentase (%)") +
theme_minimal() +
labs(
title = "%Kemiskinan Jawa Barat",
subtitle = "Tahun 2015",
x = "Longitude",
y = "Latitude"
) +
theme(legend.position = "bottom")
plot_kemiskinanExplorasi Data Area
Import Dara Area
Kita dapat membaca objek sf dengan fungsi st_read(). Misalnya, di sini kita membaca shapefile ncyang berisi kabupaten-kabupaten di North Carolina, AS, beserta nama kabupaten, jumlah kelahiran, dan jumlah kematian bayi mendadak pada tahun 1974 dan 1979.
imp <- system.file("shape/nc.shp", package = "sf")
nc <- st_read(imp, quiet = TRUE)
class(nc)[1] "sf" "data.frame"
Selanjutnya kita dapat mengidentifikasi CRS yang digunakan pada data tersebut dengan fungsi st_crs().
st_crs(nc)Coordinate Reference System:
User input: NAD27
wkt:
GEOGCRS["NAD27",
DATUM["North American Datum 1927",
ELLIPSOID["Clarke 1866",6378206.4,294.978698213898,
LENGTHUNIT["metre",1]]],
PRIMEM["Greenwich",0,
ANGLEUNIT["degree",0.0174532925199433]],
CS[ellipsoidal,2],
AXIS["latitude",north,
ORDER[1],
ANGLEUNIT["degree",0.0174532925199433]],
AXIS["longitude",east,
ORDER[2],
ANGLEUNIT["degree",0.0174532925199433]],
ID["EPSG",4267]]
plot(nc)Kita dapat membuat subset set fitur dengan menggunakan notasi tanda kurung siku dan menggunakan argumen drop untuk menghilangkan geometri.
plot(nc[9])Menghapus Beberapa Poligon
map <- nc[-which(nc$FIPS %in% c("37125", "37051")), ]
ggplot(nc) + geom_sf(aes(fill = SID79))Menghitung Jumlah Titik dalam Poligon
Kita dapat menggunakan st_intersects() untuk menghitung jumlah titik dalam poligon suatu objek sfdan st_sample() menghasilkan titik sampel acak. Objek yang dikembalikan adalah daftar dengan id fitur yang berpotongan di setiap poligon.
# Peta (objek sf)
map <- read_sf(system.file("shape/nc.shp", package = "sf"))
# Titik di atas peta (kolom daftar geometri fitur sederhana sfc)
points <- st_sample(map, size = 100)
# Peta titik di dalam poligon
ggplot() + geom_sf(data = map) + geom_sf(data = points)# Interseksi (argumen pertama peta, kemudian titik)
inter <- st_intersects(map, points)
# Menambahkan jumlah titik ke setiap poligon
map$count <- lengths(inter)
# Peta jumlah titik di dalam poligon
ggplot(map) + geom_sf(aes(fill = count))Peta Choropleth
Peta Choropleth merupakan peta visualisasi utama untuk data areal dengan memberikan gradasi warna pada setiap wilayah berdasarkan nilai peubahnya. Penggunaan nilai relatif sangat disarakankan (seperti rate, proporsi(%), atau kepadatan) karena nilai mutlak (jumlah mentah) sering kali menyesatkan jika tidak memperhitungkan ukuran populasi atau skala wilayah.
Skema Kelas dan Warna
Proses pengelompokkan data kontinu ke dalam kategori (class interval) untuk menentukan pewarnaan peta. Metode Klasifikasi : (1) Interval sama (Equal Interval), (2) Kuantil (Quantile), (3) Simpangan Baku (Standard Deviation), dan (4) Natural Breaks (Jenks)
Implementasi dalam R
Instalasi dan pemanggilan package
# Instalasi (cukup dilakukan sekali)
# install.packages(c("sf", "classInt", "tmap"))
# Memanggil package
library(sf)
library(classInt)
library(tmap)Kita gunakan data wilayah bawaan sf, yaitu North Carolina pada contoh sebelumnya. Misalnya kita ingin memetakan jumlah penduduk per county. Variabel yang dapat digunakan misalnya:
#jumlah kelahiran pada tahun 1974.
nc$BIR74 [1] 1091 487 3188 508 1421 1452 286 420 968 1612 1035 4449
[13] 1671 1556 2180 3608 1638 3146 1323 484 751 781 1269 1399
[25] 11858 16184 4672 1324 3164 7970 4021 671 3657 3609 770 1549
[37] 14484 765 4139 1207 1333 5509 3573 990 248 1946 4456 1646
[49] 3702 4606 5094 5754 7515 3999 2110 521 2692 675 870 2252
[61] 2992 6638 3776 4866 2216 1143 2648 21588 4099 1258 2356 2574
[73] 415 3589 1173 9014 533 797 3025 542 1027 20366 578 3915
[85] 1570 1494 338 2483 2756 284 5868 2255 11158 7889 2414 1782
[97] 1228 3350 5526 2181
Melihat Distribusi
summary(nc$BIR74) Min. 1st Qu. Median Mean 3rd Qu. Max.
248 1077 2180 3300 3936 21588
Tentukan jumlah kelas
Gunakan aturan Sturges:
k=1+3,322 log10(n)
dengan:
k = jumlah kelas
n = jumlah observasi/wilayah
n <- length(nc$BIR74)
k <- 1 + 3.322 * log10(n)
k <- round(k)
k[1] 8
Metode Klasifikasi
1) Equal Interval
Equal Interval (Interval Sama) digunakan untuk membagi rentang nilai atribut menjadi subrentang dengan ukuran yang sama. Equal Interval paling sesuai digunakan untuk data dengan rentang nilai yang sudah umum atau mudah dipahami, seperti persentase dan suhu. Metode ini menekankan besarnya suatu nilai atribut dibandingkan dengan nilai lainnya.
Contoh:
Jika data berkisar dari 0–300 dan ingin dibuat 3 kelas:
Lebar interval=300 / 3 =100
sehingga:
| Kelas | Rentang |
|---|---|
| 1 | 0–100 |
| 2 | 100–200 |
| 3 | 200–300 |
# Membuat klasifikasi Equal Interval
ei <- classIntervals(
nc$BIR74,
n = k,
style = "equal"
)
eistyle: equal
one of 14,887,031,544 possible partitions of this variable into 8 classes
[248,2915.5) [2915.5,5583) [5583,8250.5) [8250.5,10918) [10918,13585.5)
61 26 6 1 2
[13585.5,16253) [16253,18920.5) [18920.5,21588]
2 0 2
ei$brks[1] 248.0 2915.5 5583.0 8250.5 10918.0 13585.5 16253.0 18920.5 21588.0
Membuat Variabel Kelas
nc$kelas_ei <- cut(
nc$BIR74,
breaks = ei$brks,
include.lowest = TRUE
)
head(nc[, c("BIR74", "kelas_ei")])Simple feature collection with 6 features and 2 fields
Geometry type: MULTIPOLYGON
Dimension: XY
Bounding box: xmin: -81.74107 ymin: 36.07282 xmax: -75.77316 ymax: 36.58965
Geodetic CRS: NAD27
BIR74 kelas_ei geometry
1 1091 [248,2.92e+03] MULTIPOLYGON (((-81.47276 3...
2 487 [248,2.92e+03] MULTIPOLYGON (((-81.23989 3...
3 3188 (2.92e+03,5.58e+03] MULTIPOLYGON (((-80.45634 3...
4 508 [248,2.92e+03] MULTIPOLYGON (((-76.00897 3...
5 1421 [248,2.92e+03] MULTIPOLYGON (((-77.21767 3...
6 1452 [248,2.92e+03] MULTIPOLYGON (((-76.74506 3...
Membuat Peta
tm_shape(nc) +
tm_polygons(
"kelas_ei",
title = "Equal Interval"
)2) Quantile
Pada klasifikasi Quantile (Kuantil), setiap kelas berisi jumlah objek/wilayah (features) yang sama. Metode Quantile memberikan jumlah nilai data yang sama pada setiap kelas. Dengan demikian, tidak ada kelas yang kosong atau kelas yang memiliki jumlah nilai terlalu sedikit maupun terlalu banyak.
Namun, karena objek/wilayah dikelompokkan dalam jumlah yang sama pada setiap kelas, peta yang dihasilkan dapat terkadang menyesatkan. Objek yang memiliki nilai sangat mirip dapat ditempatkan pada kelas yang berbeda, sementara objek yang memiliki nilai sangat berbeda dapat dimasukkan ke dalam kelas yang sama. Distorsi tersebut dapat dikurangi dengan meningkatkan jumlah kelas.
Misalnya terdapat 100 kecamatan dan dibuat 5 kelas:
| Kelas | Jumlah kecamatan |
|---|---|
| Sangat rendah | 20 |
| Rendah | 20 |
| Sedang | 20 |
| Tinggi | 20 |
| Sangat tinggi | 20 |
Jadi, yang dibuat sama adalah jumlah observasinya, bukan rentang nilainya.
q <- classIntervals(
nc$BIR74,
n = k,
style = "quantile"
)
q$brks[1] 248.000 612.875 1077.000 1457.250 2180.500 3020.875 3936.000
[8] 5668.500 21588.000
nc$kelas_quantile <- cut(
nc$BIR74,
breaks = q$brks,
include.lowest = TRUE
)tm_shape(nc) +
tm_polygons(
"kelas_quantile",
title = "Quantile"
)3) Standard Deviation
Standard Deviation adalah metode klasifikasi yang mengelompokkan data berdasarkan seberapa jauh nilai suatu wilayah dari nilai rata-rata. Batas kelas ditentukan berdasarkan kelipatan tertentu dari simpangan baku, misalnya ±1 SD, ±½ SD, ±⅓ SD, atau ±¼ SD.
sd_class <- classIntervals(
nc$BIR74,
n = k,
style = "sd"
)
sd_class$brks [1] -548.5451 1375.5374 3299.6200 5223.7026 7147.7851 9071.8677
[7] 10995.9503 12920.0328 14844.1154 16768.1979 18692.2805 20616.3631
[13] 22540.4456
nc$kelas_sd <- cut(
nc$BIR74,
breaks = sd_class$brks,
include.lowest = TRUE
)tm_shape(nc) +
tm_polygons(
"kelas_sd",
title = "Standard Deviation"
)4) Natural Breaks (Jenks)
Natural Breaks (Jenks) adalah metode klasifikasi yang mencari kelompok alami dalam data. Nilai yang mirip akan dikelompokkan dalam kelas yang sama, sedangkan batas kelas ditempatkan pada bagian yang memiliki perbedaan nilai yang relatif besar. Tujuannya adalah meminimalkan perbedaan di dalam kelas dan memaksimalkan perbedaan antar kelas.
Catatan : Natural Breaks kurang tepat untuk membandingkan peta antarperiode jika batas kelas dihitung ulang untuk setiap peta, karena kelasnya mengikuti distribusi masing-masing data. Jadi, misalnya untuk membandingkan peta 2015, 2020, dan 2025, penggunaan Jenks perlu diperhatikan agar perubahan warna tidak hanya mencerminkan perubahan batas kelas
jenks <- classIntervals(
nc$BIR74,
n = k,
style = "jenks"
)
jenksstyle: jenks
one of 14,887,031,544 possible partitions of this variable into 8 classes
[248,1421] (1421,2756] (2756,4139] (4139,5868] (5868,9014]
37 24 18 10 5
(9014,11858] (11858,16184] (16184,21588]
2 2 2
jenks <- classIntervals(
nc$BIR74,
n = 5,
style = "jenks"
)
jenksstyle: jenks
one of 3,764,376 possible partitions of this variable into 5 classes
[248,2356] (2356,5094] (5094,9014] (9014,16184] (16184,21588]
55 30 9 4 2
jenks$brks[1] 248 2356 5094 9014 16184 21588
nc$kelas_jenks <- cut(
nc$BIR74,
breaks = jenks$brks,
include.lowest = TRUE
)tm_shape(nc) +
tm_polygons(
"kelas_jenks",
title = "Natural Breaks (Jenks)"
)Gabungan Semua Pendekatan Klasifikasi
library(sf)
library(tmap)
# 2. Definisikan gaya klaisfikasi
style_list <- c("equal", "quantile", "jenks", "sd")
# 3. Buat peta dalam loop
maps <- lapply(style_list, function(s) {
tm_shape(nc) +
tm_polygons(
fill = "BIR74", # Menyebutkan nama kolom variabel
fill.scale = tm_scale_intervals(
style = s,
values = "YlOrRd",
n = k
),
fill.legend = tm_legend(title = s)
) +
tm_title(paste("style =", s), size = 0.9)
})
# 4. Tampilkan 4 peta sekaligus menggunakan do.call (Aman dari out of bounds)
do.call(tmap_arrange, c(maps, list(ncol = 2)))Peta Cartogram
Kartogram adalah peta tematik yang mengubah ukuran atau bentuk wilayah secara proporsional terhadap nilai variabel tertentu. Pada peta biasa, ukuran wilayah mengikuti kondisi geografis sebenarnya. Pada kartogram, ukuran wilayah didistorsi agar lebih mencerminkan besarnya variabel yang dianalisis.
Misalnya kita ingin memetakan jumlah penduduk:
Kabupaten dengan jumlah penduduk besar → ukuran wilayah diperbesar
Kabupaten dengan jumlah penduduk kecil → ukuran wilayah diperkecil.
Sehingga kartogram menjawab pertanyaan:
“Di mana nilai variabel paling besar atau paling kecil?”
Bayangkan kita membuat peta Indonesia berdasarkan jumlah penduduk.
Pada peta geografis biasa:
Kalimantan memiliki wilayah yang sangat luas.
Jawa memiliki wilayah yang jauh lebih kecil.
Tetapi jumlah penduduk Jawa jauh lebih besar.
Jika menggunakan luas geografis, kita bisa mendapatkan kesan bahwa Kalimantan “lebih besar” secara demografis. Kartogram mencoba mengubah perspektif tersebut.
library(cartogram)
ggplot(nc) +
geom_sf(aes(fill = BIR74)) +
theme_minimal() +
labs(
title = "Jumlah Kelahiran Tahun 1974",
fill = "Jumlah"
)Pada peta ini, ukuran wilayah tidak dipengaruhi oleh jumlah kelahiran. Ukuran mengikuti kondisi geografis sebenarnya.
Karena algoritma cartogram melakukan perhitungan geometris seperti jarak, centroid, dan perubahan ukuran/bentuk wilayah sehinggamembutuhkan sistem koordinat terproyeksi. Kalau masih menggunakan longitude/latitude (derajat), jarak antar-koordinat tidak memiliki satuan panjang yang seragam seperti meter.
#Cek CRS
st_crs(nc)Coordinate Reference System:
User input: NAD27
wkt:
GEOGCRS["NAD27",
DATUM["North American Datum 1927",
ELLIPSOID["Clarke 1866",6378206.4,294.978698213898,
LENGTHUNIT["metre",1]]],
PRIMEM["Greenwich",0,
ANGLEUNIT["degree",0.0174532925199433]],
CS[ellipsoidal,2],
AXIS["latitude",north,
ORDER[1],
ANGLEUNIT["degree",0.0174532925199433]],
AXIS["longitude",east,
ORDER[2],
ANGLEUNIT["degree",0.0174532925199433]],
ID["EPSG",4267]]
#projected_crs untuk data nc yang awalnya masih berbentung geometri(long, lat)
nc_proj <- st_transform(nc, 32119)
st_crs(nc_proj)Coordinate Reference System:
User input: EPSG:32119
wkt:
PROJCRS["NAD83 / North Carolina",
BASEGEOGCRS["NAD83",
DATUM["North American Datum 1983",
ELLIPSOID["GRS 1980",6378137,298.257222101,
LENGTHUNIT["metre",1]]],
PRIMEM["Greenwich",0,
ANGLEUNIT["degree",0.0174532925199433]],
ID["EPSG",4269]],
CONVERSION["SPCS83 North Carolina zone (meter)",
METHOD["Lambert Conic Conformal (2SP)",
ID["EPSG",9802]],
PARAMETER["Latitude of false origin",33.75,
ANGLEUNIT["degree",0.0174532925199433],
ID["EPSG",8821]],
PARAMETER["Longitude of false origin",-79,
ANGLEUNIT["degree",0.0174532925199433],
ID["EPSG",8822]],
PARAMETER["Latitude of 1st standard parallel",36.1666666666667,
ANGLEUNIT["degree",0.0174532925199433],
ID["EPSG",8823]],
PARAMETER["Latitude of 2nd standard parallel",34.3333333333333,
ANGLEUNIT["degree",0.0174532925199433],
ID["EPSG",8824]],
PARAMETER["Easting at false origin",609601.22,
LENGTHUNIT["metre",1],
ID["EPSG",8826]],
PARAMETER["Northing at false origin",0,
LENGTHUNIT["metre",1],
ID["EPSG",8827]]],
CS[Cartesian,2],
AXIS["easting (X)",east,
ORDER[1],
LENGTHUNIT["metre",1]],
AXIS["northing (Y)",north,
ORDER[2],
LENGTHUNIT["metre",1]],
USAGE[
SCOPE["Engineering survey, topographic mapping."],
AREA["United States (USA) - North Carolina - counties of Alamance; Alexander; Alleghany; Anson; Ashe; Avery; Beaufort; Bertie; Bladen; Brunswick; Buncombe; Burke; Cabarrus; Caldwell; Camden; Carteret; Caswell; Catawba; Chatham; Cherokee; Chowan; Clay; Cleveland; Columbus; Craven; Cumberland; Currituck; Dare; Davidson; Davie; Duplin; Durham; Edgecombe; Forsyth; Franklin; Gaston; Gates; Graham; Granville; Greene; Guilford; Halifax; Harnett; Haywood; Henderson; Hertford; Hoke; Hyde; Iredell; Jackson; Johnston; Jones; Lee; Lenoir; Lincoln; Macon; Madison; Martin; McDowell; Mecklenburg; Mitchell; Montgomery; Moore; Nash; New Hanover; Northampton; Onslow; Orange; Pamlico; Pasquotank; Pender; Perquimans; Person; Pitt; Polk; Randolph; Richmond; Robeson; Rockingham; Rowan; Rutherford; Sampson; Scotland; Stanly; Stokes; Surry; Swain; Transylvania; Tyrrell; Union; Vance; Wake; Warren; Washington; Watauga; Wayne; Wilkes; Wilson; Yadkin; Yancey."],
BBOX[33.83,-84.33,36.59,-75.38]],
ID["EPSG",32119]]
nc_cartogram <- cartogram_cont(
nc_proj,
weight = "BIR74"
)Implementasi Peta Cartogram di R
library(cartogram)
nc_cartogram <- cartogram_cont(
nc_proj,
weight = "BIR74"
)ggplot(nc_cartogram) +
geom_sf(aes(fill = BIR74)) +
theme_void() +
labs(
title = "Contiguous Cartogram",
subtitle = "Ukuran wilayah berdasarkan jumlah kelahiran 1974",
fill = "Jumlah"
)Kartogram digunakan ketika fokus utama adalah menunjukkan besarnya suatu variabel, bukan mempertahankan bentuk geografis wilayah.
Dan langsung beri peringatan:
⚠️ Keterbatasan: semakin besar distorsi geometris, semakin sulit pembaca mengenali lokasi dan hubungan spasial antarwilayah.
Menambahkan label pada peta
#Label pada top10
top10 <- nc_cartogram |>
dplyr::arrange(desc(BIR74)) |>
dplyr::slice_head(n = 10)
ggplot(nc_cartogram) +
geom_sf(aes(fill = BIR74)) +
geom_sf_text(
data = top10,
aes(label = NAME),
size = 3
) +
theme_void() +
labs(
title = "Contiguous Cartogram",
subtitle = "10 wilayah dengan jumlah kelahiran tertinggi",
fill = "Jumlah"
)Isu Data Areal
1. MAUP (Modifiable Areal Unit Problem) MAUP terjadi ketika hasil statistik atau analisis spasial berubah-ubah akibat perubahan batas administrasi/wilayah pengamatan. MAUP terdiri dari dua efek utama:
Efek Skala (Scale Effect): Hasil analisis berubah jika unit wilayah digabungkan (aggregated) ke tingkat yang lebih besar
Efek Zonasi (Zoning Effect): Hasil analisis berubah jika bentuk atau susunan batas wilayah diubah, meskipun agregasi/jumlah wilayahnya sama.
2. MIDP (Modifiable Individual Data Problem) Mirip dengan MAUP, namun berfokus pada titik data level individu (point data) yang dikelompokkan ke dalam area statistik buatan.
3. Bias Ekologis (Ecological Fallacy / Bias) Kesalahan logika saat mengambil kesimpulan mengenai individu berdasarkan hasil analisis dari data agregat (level kelompok/area). Contoh: Kabupaten A memiliki rerata pendapatan tinggi, sehingga dapat disimpulkan seluruh warga di Kabupaten A pasti kaya (ini adalah bias ekologis).
Ilustrasi MAUP
a) Efek Skala
Contoh kasus yaitu mengagregasikan data spasial dari level Desa (skala kecil) ke level Kecamatan (skala besar) menggunakan paket sfdan dplyr. Hal ini untuk mengilustrasikan bagaimana nilai rata-rata/korelasi berubah akibat Efek Skala (MAUP).
library(patchwork)
#Membuat Data Spasial Buatan (Grid 4x4 = 16 Desa)
set.seed(123)
# Buat bounding box dasar
bbox_dasar <- st_bbox(c(xmin = 0, ymin = 0, xmax = 4, ymax = 4), crs = 4326)
# Buat grid berdasarkan bbox dasar
grid <- st_make_grid(
x = bbox_dasar,
cellsize = c(1, 1),
n = c(4, 4)
) %>% st_sf()
# Menambahkan atribut data simulasi level Desa
data_desa <- grid %>%
mutate(
desa_id = paste0("Desa_", 1:n()),
# Mengelompokkan 16 Desa menjadi 4 Kecamatan (2x2 grid)
kecamatan_id = paste0("Kecamatan_", c(1, 1, 2, 2, 1, 1, 2, 2, 3, 3, 4, 4, 3, 3, 4, 4)),
pendapatan = round(runif(n(), min = 20, max = 100), 2),
kemiskinan = round(100 - pendapatan + rnorm(n(), mean = 0, sd = 5), 2)
)# Agregasi Data ke Level Kecamatan (Efek Skala)
data_kecamatan <- data_desa %>%
group_by(kecamatan_id) %>%
summarise(
pendapatan_mean = mean(pendapatan),
kemiskinan_mean = mean(kemiskinan),
.groups = "drop"
)# Bandingkan Statistik & Korelasi (Level Desa vs. Kecamatan)
#==================================================
#EFEK SKALA MAUP: HASIL ANALISIS
#==================================================
kor_desa <- cor(data_desa$pendapatan, data_desa$kemiskinan)
kor_desa[1] -0.9799158
kor_kec <- cor(data_kecamatan$pendapatan_mean, data_kecamatan$kemiskinan_mean)
kor_kec[1] -0.847398
# Visualisasi Perubahan Skala Wilayah
plot_desa <- ggplot(data_desa) +
geom_sf(aes(fill = pendapatan), color = "white") +
geom_sf_text(aes(label = desa_id), size = 2.5) +
scale_fill_viridis_c(option = "magma") +
labs(title = "Skala Asli: Desa (16 Unit)", fill = "Pendapatan") +
theme_minimal()
plot_kecamatan <- ggplot(data_kecamatan) +
geom_sf(aes(fill = pendapatan_mean), color = "white") +
geom_sf_text(aes(label = kecamatan_id), size = 3, fontface = "bold") +
scale_fill_viridis_c(option = "magma") +
labs(title = "Skala Agregat: Kecamatan (4 Unit)", fill = "Rerata") +
theme_minimal()
plot_desa + plot_kecamatanb) Efek Zonasi
# 2. Membuat Data Spasial Buatan (Grid 4x4 = 16 Desa)
set.seed(123)
bbox_dasar <- st_bbox(c(xmin = 0, ymin = 0, xmax = 4, ymax = 4), crs = 4326)
grid <- st_make_grid(
x = bbox_dasar,
cellsize = c(1, 1),
n = c(4, 4)
) %>% st_sf()
# Menambahkan atribut data simulasi & 2 Skema Zonasi Berbeda (Sama-sama 4 Unit)
data_desa <- grid %>%
mutate(
desa_id = paste0("D", 1:n()),
# ZONASI A: Skema Grid 2x2 (4 Kecamatan)
zona_grid = paste0("Zona_A_", c(1,1,2,2, 1,1,2,2, 3,3,4,4, 3,3,4,4)),
# ZONASI B: Skema Strip Horizontal 4x1 (4 Kecamatan)
zona_horiz = paste0("Zona_B_", c(1,1,1,1, 2,2,2,2, 3,3,3,3, 4,4,4,4)),
# Data Atribut Simulasi
pendapatan = round(runif(n(), min = 20, max = 100), 2),
kemiskinan = round(100 - pendapatan + rnorm(n(), mean = 0, sd = 5), 2)
)
head(data_desa)Simple feature collection with 6 features and 5 fields
Geometry type: POLYGON
Dimension: XY
Bounding box: xmin: 0 ymin: 0 xmax: 4 ymax: 2
Geodetic CRS: WGS 84
geometry desa_id zona_grid zona_horiz pendapatan
1 POLYGON ((0 0, 1 0, 1 1, 0 ... D1 Zona_A_1 Zona_B_1 43.01
2 POLYGON ((1 0, 2 0, 2 1, 1 ... D2 Zona_A_1 Zona_B_1 83.06
3 POLYGON ((2 0, 3 0, 3 1, 2 ... D3 Zona_A_2 Zona_B_1 52.72
4 POLYGON ((3 0, 4 0, 4 1, 3 ... D4 Zona_A_2 Zona_B_1 90.64
5 POLYGON ((0 1, 1 1, 1 2, 0 ... D5 Zona_A_1 Zona_B_2 95.24
6 POLYGON ((1 1, 2 1, 2 2, 1 ... D6 Zona_A_1 Zona_B_2 23.64
kemiskinan
1 53.56
2 14.71
3 53.40
4 11.16
5 6.76
6 76.91
# =============================================================================
# 3. AGREGASI DATA BERDASARKAN DUA SKEMA ZONASI
# =============================================================================
# Agregasi Zonasi A (Grid 2x2)
data_zona_A <- data_desa %>%
group_by(zona_grid) %>%
summarise(
pendapatan = mean(pendapatan),
kemiskinan = mean(kemiskinan),
.groups = "drop"
)
# Agregasi Zonasi B (Strip Horizontal 4x1)
data_zona_B <- data_desa %>%
group_by(zona_horiz) %>%
summarise(
pendapatan = mean(pendapatan),
kemiskinan = mean(kemiskinan),
.groups = "drop"
)# =============================================================================
# 4. HASIL PERBANDINGAN STATISTIK (EFEK ZONASI)
# ==============================================================================
r_desa <- cor(data_desa$pendapatan, data_desa$kemiskinan)
r_desa[1] -0.9799158
r_zona_A <- cor(data_zona_A$pendapatan, data_zona_A$kemiskinan)
r_zona_A[1] -0.847398
r_zona_B <- cor(data_zona_B$pendapatan, data_zona_B$kemiskinan)
r_zona_B[1] 0.2049043
# =============================================================================
# 5. VISUALISASI PERBANDINGAN ZONASI
# =============================================================================
plot_A <- ggplot(data_zona_A) +
geom_sf(aes(fill = pendapatan), color = "white") +
geom_sf_text(aes(label = zona_grid), size = 3, fontface = "bold") +
scale_fill_viridis_c(option = "magma") +
labs(title = "Zonasi A: Grid (2x2)", subtitle = "4 Unit Wilayah", fill = "Rerata") +
theme_minimal()
plot_B <- ggplot(data_zona_B) +
geom_sf(aes(fill = pendapatan), color = "white") +
geom_sf_text(aes(label = zona_horiz), size = 3, fontface = "bold") +
scale_fill_viridis_c(option = "magma") +
labs(title = "Zonasi B: Horizontal (4x1)", subtitle = "4 Unit Wilayah", fill = "Rerata") +
theme_minimal()
# Tampilkan visualisasi berdampingan
plot_A + plot_BIlustrasi MIDP
Misalkan dalam menganalisis korelasi antara Tingkat Curah Hujan (mm) dan Jumlah Kasus Demam Berdarah (DBD) di suatu kota.
Agregasi Harian: Hubungan tidak terlihat atau korelasi sangat rendah (r≈0.05) karena ada time lag (masa inkubasi nyamuk butuh waktu beberapa hari/minggu setelah hujan).
Agregasi Mingguan: Hubungan mulai terlihat (r≈0.45).
Agregasi Bulanan: Korelasi menjadi sangat kuat (r≈0.82) karena variasi harian (noise) sudah terratakan.
Poin Inti: Fenomena fenomena spasial-temporal yang sama dapat menghasilkan kesimpulan statistik yang bertolak belakang hanya karena perubahan skala interval waktu pengamatan.
library(lubridate)
set.seed(42)
# Buat Data Harian selama 1 Tahun (365 Hari)
data_harian <- data.frame(
tanggal = seq(as.Date("2024-01-01"), as.Date("2024-12-31"), by = "day")
) %>%
mutate(
bulan = floor_date(tanggal, "month"),
# Simulasi Curah Hujan Harian
curah_hujan = round(rpois(n(), lambda = 20) + sin(1:n()/30)*10, 1),
# Kasus DBD dipengaruhi hujan tetapi ada noise harian yang tinggi
kasus_dbd = round(curah_hujan * 0.3 + rnorm(n(), mean = 10, sd = 8), 0)
)
head(data_harian) tanggal bulan curah_hujan kasus_dbd
1 2024-01-01 2024-01-01 26.3 15
2 2024-01-02 2024-01-01 17.7 16
3 2024-01-03 2024-01-01 21.0 14
4 2024-01-04 2024-01-01 16.3 28
5 2024-01-05 2024-01-01 20.7 22
6 2024-01-06 2024-01-01 28.0 42
# Agregasi ke Level Bulanan
data_bulanan <- data_harian %>%
group_by(bulan) %>%
summarise(
hujan_mean = mean(curah_hujan),
dbd_mean = mean(kasus_dbd),
.groups = "drop"
)
head(data_bulanan)# A tibble: 6 × 3
bulan hujan_mean dbd_mean
<date> <dbl> <dbl>
1 2024-01-01 26.0 16.6
2 2024-02-01 30.5 18.7
3 2024-03-01 25.3 19
4 2024-04-01 14.9 13.8
5 2024-05-01 9.89 13.0
6 2024-06-01 13.2 15.7
# 3. Hitung Korelasi
r_harian <- cor(data_harian$curah_hujan, data_harian$kasus_dbd)
r_harian[1] 0.3054636
r_bulanan <- cor(data_bulanan$hujan_mean, data_bulanan$dbd_mean)
r_bulanan[1] 0.8511978
# 4. Visualisasi Scatter Plot
p_harian <- ggplot(data_harian, aes(x = curah_hujan, y = kasus_dbd)) +
geom_point(alpha = 0.4, color = "darkblue") +
geom_smooth(method = "lm", se = FALSE, color = "red") +
labs(title = "Skala Harian", subtitle = paste0("r = ", round(r_harian, 2))) +
theme_minimal()
p_bulanan <- ggplot(data_bulanan, aes(x = hujan_mean, y = dbd_mean)) +
geom_point(size = 3, color = "firebrick") +
geom_smooth(method = "lm", se = FALSE, color = "red") +
labs(title = "Skala Bulanan (Agregat)", subtitle = paste0("r = ", round(r_bulanan, 2))) +
theme_minimal()
p_harian + p_bulanan