Join Tabel dan Peta Choropleth

Praktikum 3 STA581 Sains Data

Nur Andi Setiabudi1

2021-09-09

Artikel ini juga dipublikasikan di RPubs.com/nurandi.

Bagian A: Join Tabel

Dalam melakukan analisis, sering kali kita memerlukan data yang berasal tidak hanya dari satu tabel saja namun dari gabungan beberapa tabel sekaligus. Untuk menggabungkan dua atau lebih tabel, kita dapat menggunakan perintah join, merge atau sejenisnya. Perintah ini akan menggabungkan beberapa tabel berdasarkan kecocokan nilai dari satu atau lebih kolom tertentu (yang biasanya disebut sebagai key atau kolom kunci). Dalam R, proses join dapat dilakukan dengan mudah menggunakan paket dplyr (Wickham et al. 2021) yang merupakan bagian dari tidyverse (Wickham et al. 2019). Selain itu, dapat juga menggunakan perintah-perintah join dalam SQL apabila R sudah terhubung dengan engine SQL/RDBMS.

Paket dplyr menyediakan beberapa perintah join yang dapat digunakan sesuai dengan output yang diinginkan (Wickham and Grolemund 2017), yaitu

  • inner_join()
  • semi_join()
  • left_join()
  • right_join()
  • full_join()
  • anti_join()

Perbedaan dari fungsi-fungsi tersebut dapat diringkas dalam diagram venn berikut.

Join dalam dplyr

Untuk praktik penggabungan tabel menggunakan paket dplyr, kita akan memanfaatkan data Northwind di SQLite3. Database Northwind, disediakan oleh Microsoft untuk SQL Server, menggambarkan database milik suatu perusahaan fiktif yang bernama “Northwind Traders” yang bergerak bidang ekspor-impor. Dalam database ini terdapat beberapa tabel seperti tabel Customers, Orders, Suppliers dan lain sebagainya yang saling terhubung seperti digambarkan dalam ERD (Entity Relationship Diagram) berikut ini:

ERD Northwind

Persiapan Data dan Paket

Latihan ini akan menggunakan beberapa paket, di antaranya DBI (R Special Interest Group on Databases (R-SIG-DB), Wickham, and Müller 2021) dan RSQLite (Müller et al. 2021) untuk koneksi ke database, dplyr atau tidyverse untuk pengolahan data, dan knitr untuk mengatur output dari markdown. Jika belum terinstall, silakan install terlebih dahulu.

# opsional, jika belum terinstall
install.packages(c("RSQLite", "DBI", "dplyr"), dependencies = TRUE)

Lalu load ke dalam workspace

library(dplyr)
library(RSQLite)

Selanjutnya, buat connection object yang akan menghubungkan R dengan database/file SQLite. Dalam hal ini, file SQLite adalah Northwind_large.sqlite yang ada pada folder Northwind di working directory.

conn <- dbConnect(SQLite(), "Northwind/Northwind_large.sqlite")
class(conn)
## [1] "SQLiteConnection"
## attr(,"package")
## [1] "RSQLite"

Untuk memeriksa apakah sudah terkoneksi dengan benar, kita lihat tabel apa saja yang ada dalam database.

# list table
RSQLite::dbListTables(conn)
##  [1] "Category"             "Customer"             "CustomerCustomerDemo"
##  [4] "CustomerDemographic"  "Employee"             "EmployeeTerritory"   
##  [7] "Order"                "OrderDetail"          "Product"             
## [10] "ProductDetails_V"     "Region"               "Shipper"             
## [13] "Supplier"             "Territory"

Setelah itu kita buat tabel dari SQLite untuk diolah dengan dplyr

Order <- tbl(conn, "Order")
OrderDetail <- tbl(conn, "OrderDetail")
Customer <- tbl(conn, "Customer")
Supplier <- tbl(conn, "Supplier")
Product <- tbl(conn, "Product")
Shipper <- tbl(conn, "Shipper")

Inner Join: inner_join()

Inner join menggabungkan dua tabel di mana kolom key saling bersesuaian, atau ditemukan di kedua tabel. Dengan inner join, tabel akan digabungkan dua arah, sehingga tidak ada data yang kosong di salah satu sisi. Perintah inner join di dplyr adalah inner_join() dan akan mengeluarkan kolom-kolom dari kedua tabel.

Contoh 1: Menghitung jumlah order dan total amount (dari tabel OrderDetail) berdasarkan shipping region (dari tabel Order).

Order %>%
  inner_join(OrderDetail, by = c("Id" = "OrderId")) %>%
  group_by(ShipRegion) %>%
  summarise(TotalOrder = n_distinct(Id.x),
            TotalAmount = sum(UnitPrice*Quantity)) %>%
  ungroup()

Contoh 2: Menghitung jumlah order dan total amount (dari tabel OrderDetail) berdasarkan perusahaan shipper (dari tabel Shipper). Meskipun kolom-kolom tersebut berasal dari dua tabel, kita membutuhkan tabel Order untuk menghubungkan keduanya. Sehingga dilakukan dua kali join.

Order %>%
  inner_join(OrderDetail, by = c("Id" = "OrderId")) %>%
  inner_join(Shipper, by = c("ShipVia" = "Id")) %>%
  group_by(ShipperCompanyName = CompanyName) %>%
  summarise(TotalOrder = n(),
            TotalAmount = sum(UnitPrice*Quantity)) %>%
  ungroup()

Semi Join: semi_join()

Semi join mirip dengan inner join. Hanya saja, pada semi join, tidak menghasilkan output yang duplikat. Perintah semi join di dplyr adalah semi_join() dan hanya mengeluarkan kolom-kolom dari tabel pertama.

Contoh: Menghitung jumlah pelanggan (dari tabel Customer) yang pernah melakukan transaksi (dari tabel Order).

Customer %>%
  semi_join(Order, by = c("Id" = "CustomerId")) %>%
  group_by(Region) %>%
  summarise(TotaCustomerWithOrder = n()) %>%
  ungroup()

Perhatikan perbedaannya dengan hasil inner join di contoh sebelumnya.

Left Join: left_join()

Left join menggabungkan dua tabel dengan mengambil seluruh baris dari tabel pertama (tabel kiri/left). Jika ada key yang sesuai di kolom kedua, maka kolom-kolomnya akan diambil. Sedangkan kolom-kolom lain di mana key tidak sesuai akan diisi dengan NA (di SQL diisi dengan NULL). Dengan left join, tabel akan digabungkan bersifat satu arah, sehingga ada kemungkinan data kosong di tabel kedua. Perintah left join di dplyr adalah left_join() dan akan mengeluarkan kolom-kolom dari kedua tabel.

Contoh 1: Menghitung jumlah order (dari tabel Order) berdasarkan regional asal pelanggan (dari tabel Customer). Karena tidak semua transaksi dilakukan oleh pelanggan terdaftar, maka memungkinkan diperoleh regional yang kosong.

Order %>%
  left_join(Customer,  by = c("CustomerId" = "Id")) %>%
  group_by(Region) %>%
  summarise(TotalOrder = n()) %>%
  ungroup()

Contoh 2: Menghitung jumlah order (dari tabel Order) berdasarkan regional asal pelanggan (dari tabel Customer) dan perusahaan shipper (dari tabel Shipper). Karena ada tiga tabel, kita dapat lakukan dua kali join.

Order %>%
  left_join(Customer,  by = c("CustomerId" = "Id")) %>%
  inner_join(Shipper, by = c("ShipVia" = "Id")) %>%
  group_by(Region,  
           ShipperCompanyName = CompanyName.y) %>%
  summarise(TotalOrder = n()) %>%
  ungroup()

Right Join: right_join()

Berkebalikan dengan left join, perintah right join akan menampilkan semua baris dari tabel kedua (tabel kanan/right). Jika ada key yang sesuai di kolom pertama, maka kolom-kolomnya akan diambil. Jika key tidak bersesuaian, maka akan diisi dengan NA. Perintah left join di dplyr adalah right_join() dan akan mengeluarkan kolom-kolom dari kedua tabel.

Contoh: Kedua perintah right_join() berikut akan menghasilkan output yang sama seperti contoh left_join() di atas.

Customer %>%
  right_join(Order,  by = c("Id" = "CustomerId")) %>%
  group_by(CustomerRegion = Region) %>%
  summarise(TotalOrder = n()) %>%
  ungroup()
Customer %>%
  right_join(Order,  by = c("Id" = "CustomerId")) %>%
  inner_join(Shipper, by = c("ShipVia" = "Id")) %>%
  group_by(CustomerRegion = Region,  ShipperCompanyName = CompanyName.y) %>%
  summarise(TotalOrder = n()) %>%
  ungroup()

Full Join: full_join()

Berbeda dengan dengan left ataupun right join, perintah full join atau full outer join akan menampilkan semua baris dari kedua tabel. Jika key yang besesuaian maka data dari kolom-kolomnya akan diambil. Jika key tidak saling besesuaian, maka kedua sisi akan diisi dengan NA. Perintah full outer join di dplyr adalah full_join() dan akan mengeluarkan kolom-kolom dari kedua tabel.

Contoh 1: Menghitung jumlah order (dari tabel Order) berdasarkan regional asal pelanggan (dari tabel Customer) dan regional tujuan pengiriman (dari tabel Order). Karena tidak semua transaksi dilakukan oleh pelanggan terdaftar, dan diasumsikan tidak semua pelanggan penah melakukan transaksi, maka memungkinkan diperoleh regional pelanggan dan regional tujuan yang kosong.

Customer %>%
  full_join(Order,  by = c("Id" = "CustomerId")) %>%
  group_by(CustomerRegion = Region, ShipRegion) %>%
  summarise(TotalOrder = n()) %>%
  ungroup()

Ternyata semua transaksi dilakukan oleh pelanggan-pelanggan yang ada di tabel Customer sehingga hasil dari perintah full outer join di atas sama seperti left join di contoh sebelumnya.

Contoh 2: Menghitung jumlah pelanggan (dari tabel Customer) dan jumlah supplier (dari tabel Supplier) berdasarkan regional masing-masing. Pelanggan maupun supplier tidak berasal dari semua regional, sehingga ada kemungkinan ada regional yang kosong di salah satu sisi. Untuk meringkas peringah, kita terlebih dahulu membuat tabel agregasi berikut.

CustomerByRegion <- Customer %>% 
  group_by(Region) %>%
  summarise(TotalCustomer = n()) %>%
  ungroup()

SupplierByRegion <- Supplier %>%
  group_by(Region) %>%
  summarise(TotalSupplier = n()) %>%
  ungroup()

Baru kemudian lakukan proses join.

CustomerByRegion %>%
  full_join(SupplierByRegion)

Perintah di atas dapat dijalankan tanpa membuat tabel agregasi terlebih dahulu, sebagai berikut.

Customer %>% 
  group_by(Region) %>%
  summarise(TotalCustomer = n()) %>%
  ungroup() %>%
  full_join(
    Supplier %>%
      group_by(Region) %>%
      summarise(TotalSupplier = n()) %>%
      ungroup() 
  )

Anti Join: anti_join()

Anti join berfungsi untuk menampilkan semua baris di tabel pertama yang key-nya tidak saling bersesuaian dengan tabel kedua. Perintah anti join di dplyr adalah anti_join() dan hanya menampilkan kolom-kolom dari tabel pertama.

Contoh 1: Menggunakan tabel yang dibuat pada contoh sebelumnya, akan ditampilkan regional dan jumlah pelanggannya, yang tidak ada di tabel regional supplier.

CustomerByRegion %>%
  anti_join(SupplierByRegion)

Contoh 2: Anti join dapat diperoleh dengan memodifikasi full outer join sebagai berikut

CustomerByRegion %>%
  full_join(SupplierByRegion) %>%
  filter(is.na(TotalSupplier)) %>%
  select(-TotalSupplier)
Order %>%
  anti_join(Customer, by = c("CustomerId" = "Id")) %>%
  group_by(ShipRegion) %>%
  summarise(TotalOrder = n())

Join dengan SQLite

Join merupakan salah satu fungsi utama dalam SQL. Namun tidak semua RDBMS dilengkapi dengan fungsi join yang lengkap. Fungsi standar yang biasanya ada di banyak RDBMS antara lain inner join, left join, right join, dan full outer join. Anti join dan semi join jarang dijumpai di RDBMS. Bahkan di SQLite hanya ada inner join dan left join saja. Meskipun demikian, bukan berarti kita tidak bisa melakukan proses join seperti yang bisa dilakukan dplyr. Dengan sedikit langkah tambahan, seluruh proses join memungkinkan untuk dilakukan.

Di RMarkdown, kita bisa langsung terhubung dengan database dan melakukan perintah SQL. Caranya, perintah SQL harus di letakan di dalam chunk atau blok kode SQL {sql connection=connectioName}

Inner Join

Hampir semua RDBMS termasuk SQLite mempunyai fungsi untuk operasi inner join, yaitu JOIN. Perhatikan tabel yang akan kita gunakan salah satunya bernama Order yang merupakan nama salah satu perintah di SQL. Agar Order dikenali sebagai tabel, bukan sebagai perintah, maka harus diapit oleh double-quote: "Order".

SELECT o.ShipRegion
  , count(*) TotalOrder
  , sum(od.UnitPrice * od.Quantity) TotalAmount
FROM "Order" o
JOIN OrderDetail od
ON o.Id = od.OrderId
GROUP BY o.ShipRegion;
9 records
ShipRegion TotalOrder TotalAmount
British Isles 56850 42063804
Central America 34979 25735604
Eastern Europe 6756 4874972
North America 108711 80065723
Northern Europe 27911 20688201
Scandinavia 21286 15469026
South America 111024 81867806
Southern Europe 58906 43372586
Western Europe 195460 143817967

Left Join

Seperti inner join, hampir semua RDBMS mempunyai fungsi untuk operasi left join, yaitu LEFT JOIN.

-- Left join
SELECT c.Region CustomerRegion
  , COUNT(*) TotalOrder
FROM "Order" o
LEFT JOIN Customer c
ON o.CustomerId = c.Id
GROUP BY c.Region;
Displaying records 1 - 10
CustomerRegion TotalOrder
NA 29
British Isles 1445
Central America 908
Eastern Europe 187
North America 2936
Northern Europe 782
Scandinavia 545
South America 3050
Southern Europe 1770
Western Europe 5166

Semi Join

SQLite tidak dilengkapi dengan perintah semi join. Untuk melakukan semi join, kita bisa menggunakan inner join tabel pertama dengan tabel kedua hasil distinct.

SELECT c.Region
  , COUNT(*) TotalCustomerWithOrder
FROM Customer c
JOIN (
  SELECT DISTINCT CustomerId
  FROM "Order" ) o
ON c.Id = o.CustomerId
GROUP BY c.Region
9 records
Region TotalCustomerWithOrder
British Isles 8
Central America 5
Eastern Europe 1
North America 16
Northern Europe 4
Scandinavia 3
South America 16
Southern Europe 10
Western Europe 28

Right Join

SQLite tidak dilengkapi dengan perintah right join. Untuk melakukan right join, kita bisa menggunakan left join dengan menukar posisi tabel.

SELECT c.Region CustomerRegion
  , COUNT(*) TotalOrder
FROM "Order" o
LEFT JOIN Customer c
ON o.CustomerId = c.Id
GROUP BY c.Region;
Displaying records 1 - 10
CustomerRegion TotalOrder
NA 29
British Isles 1445
Central America 908
Eastern Europe 187
North America 2936
Northern Europe 782
Scandinavia 545
South America 3050
Southern Europe 1770
Western Europe 5166

Full Outer Join

Meskipun banyak RDBMS dilengkapi perintah full outer join, SQLite ternyata tidak. Untuk melakukan full outer join, melakukan dua kali left join dan menggabungkan baris-baris hasilnya dengan perintah UNION.

SELECT c.*, s.TotalSupplier
FROM (
  SELECT Region
    , COUNT(*) TotalCustomer
  FROM Customer
  GROUP BY Region ) c
LEFT JOIN (
  SELECT Region
    , COUNT(*) TotalSupplier
  FROM Supplier
  GROUP BY Region ) s
ON c.Region = s.Region

UNION ALL

SELECT s.Region, c.TotalCustomer, s.TotalSupplier
FROM (
  SELECT Region
    , COUNT(*) TotalSupplier
  FROM Supplier
  GROUP BY Region ) s
LEFT JOIN (
  SELECT Region
    , COUNT(*) TotalCustomer
  FROM Customer
  GROUP BY Region ) c
ON c.Region = s.Region
WHERE c.TotalCustomer IS NULL;
Displaying records 1 - 10
Region TotalCustomer TotalSupplier
British Isles 8 2
Central America 5 NA
Eastern Europe 1 NA
North America 16 6
Northern Europe 4 4
Scandinavia 3 2
South America 16 1
Southern Europe 10 3
Western Europe 28 6
Eastern Asia NA 2

Anti Join

SQLite tidak dilengkapi dengan perintah anti join. Untuk melakukan anti join, kita bisa menggunakan left join dan hanya mengambil baris-baris di mana kolom key di tabel kedua kosong atau NULL (dengan perintah WHERE).

SELECT o.ShipRegion
  , COUNT(*) TotalOrder
FROM "Order" o
LEFT JOIN Customer c
ON o.CustomerId = c.Id
WHERE c.Id IS NULL
GROUP BY o.ShipRegion
3 records
ShipRegion TotalOrder
Central America 7
South America 18
Western Europe 4

Bagian B: Peta Choropleth

Data Spasial

Data spasial merupakan data yang merepresentasikan gambaran kejadian di permukaan bumi yang disajikan dalam bentuk peta, grafik dan gambar berformat digital. Data spasial tidak hanya terdiri dari baris dan kolom seperti data tabular pada umumnya, tetapi juga mencakup informasi geometris objek seperti titik (koordinat), garis, dan poligon atau area. Format yang paling umum digunakan untuk data spasial adalah shapefiles yang dikembangkan oleh ESRI. Format lain yang bisa dipakai antara lain GeoJSON dan GeoPackage. Pada latihan ini akan fokus pada data spasial yang disimpan dalam format shapefiles.

Shapefiles merupakan format vektor dari data spasial yang terdiri dari beberapa ekstensi file (Rahma Anisa 2021), yaitu:

  • Shapefile shape (.shp)
  • Shapefile shape index (.shx)
  • Shapefile attribute (.dbf)

Format shapefiles merupakan format yang sering banyak digunakan. Sehingga berbagai software yang mendukung analisis data spasial biasanya dapat digunakan untuk membuka dan mengelola shapefiles, termasuk R.

Data spasial biasanya disajikan dalam bentuk peta tematik. Salah satu jenis peta yang banyak digunakan adalah peta choropleth, yaitu jenis peta tematik yang merepresentasikan data/statistik menggunakan berbagai gradasi warna dan simbol di atas area area geografis tertentu (misalnya negara, provinsi, kabupaten dan lain-lain)(Rahmi 2021).

Membaca Data Spasial

Pada praktikum ini akan disajikan peta choropleth sebaran penduduk di Kota Bogor berdasarkan kecamatan. Shapefile diperoleh dari website data.humdata.org (HDX 2020). Sedangkan data penduduk diperoleh dari Visualisasi Data Kependudukan Kementerian Dalam Negeri (Kemendagri 2020).

Dalam R, ada beberapa paket yang dapat digunakan untuk membaca dan mengelola data spasial yaitu sf(Pebesma 2018) dan rgdal. Silakan install salah satu paket tersebut terlebih dahulu, lalu load ke dalam working space.

# opsional, jika belum terinstall
install.packages("sf")
library(sf)

Membaca data dapat dilakukan dengan menggunakan fungsi st_read(). Gambaran data dapat ditampilkan dengan fungsi glimpse() dari paket dplyr yang sudah kita persiapkan sebelumnya.

mapIndonesia <- st_read("map/shp/idn_admbnda_adm3_bps_20200401.shp", 
                        quiet = TRUE)
glimpse(mapIndonesia)
## Rows: 7,069
## Columns: 17
## $ Shape_Leng <dbl> 0.2798656, 0.7514001, 0.6900061, 0.6483629, 0.2437073, 1.35~
## $ Shape_Area <dbl> 0.003107633, 0.016925540, 0.024636382, 0.010761277, 0.00116~
## $ ADM3_EN    <chr> "2 X 11 Enam Lingkung", "2 X 11 Kayu Tanam", "Abab", "Abang~
## $ ADM3_PCODE <chr> "ID1306050", "ID1306052", "ID1612030", "ID5107050", "ID7471~
## $ ADM3_REF   <chr> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA,~
## $ ADM3ALT1EN <chr> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA,~
## $ ADM3ALT2EN <chr> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA,~
## $ ADM2_EN    <chr> "Padang Pariaman", "Padang Pariaman", "Penukal Abab Lematan~
## $ ADM2_PCODE <chr> "ID1306", "ID1306", "ID1612", "ID5107", "ID7471", "ID9432",~
## $ ADM1_EN    <chr> "Sumatera Barat", "Sumatera Barat", "Sumatera Selatan", "Ba~
## $ ADM1_PCODE <chr> "ID13", "ID13", "ID16", "ID51", "ID74", "ID94", "ID94", "ID~
## $ ADM0_EN    <chr> "Indonesia", "Indonesia", "Indonesia", "Indonesia", "Indone~
## $ ADM0_PCODE <chr> "ID", "ID", "ID", "ID", "ID", "ID", "ID", "ID", "ID", "ID",~
## $ date       <date> 2019-12-20, 2019-12-20, 2019-12-20, 2019-12-20, 2019-12-20~
## $ validOn    <date> 2020-04-01, 2020-04-01, 2020-04-01, 2020-04-01, 2020-04-01~
## $ validTo    <date> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA~
## $ geometry   <MULTIPOLYGON [°]> MULTIPOLYGON (((100.2811 -0..., MULTIPOLYGON (~

Terlihat bahwa data mapIndonesia hanya berisi informasi geografis. Untuk itu kita membutuhkan data tambahan.

dataBogor <- read.csv("data/Demografi-Bogor.csv")
dataBogor

Selanjutnya gabungkan kedua data tersebut menjadi satu. Karena kita hanya ingin menampilkan peta Kota Bogor, maka kita bisa gunakan fungsi inner_join() berdasarkan kolom ADM3_PCODE dari mapIndonesia dan kolom KodeBPS dari dataBogor.

mapBogor<- mapIndonesia %>%
  inner_join(dataBogor, by = c("ADM3_PCODE" = "KodeBPS"))
mapBogor

Peta Statis dengan ggplot2

Data spasial dan informasi jumlah penduduk kota bogor sudah tersedia dalam satu dataframe. Untuk menyajikan dalam peta, beberapa paket R dapat digunakan. Untuk jenis peta statis (biasanya untuk tujuan dicetak) paket ggplot2 (Wickham 2016) bisa menjadi pilihan yaitu dengan fungsi geom_sf(). Paket ggplot merupakan bagian dari tidyverse sehingga jika sudah terinstal, tidak perlu lagi menginstal paketnya.

# opsional, jika belum terinstal
install.packages("ggplot2")
library(ggplot2)

Konsep dari ggplot2 dalam visualisasi data adalah dengan mengkombinasikan data, stat dan geom atau layer. Pada perintah berikut, ggplot() akan menginisiasi layer kosong yang siap ditambahkan layer-layer lain diatasnya. Lalu geom_sf() akan memetakan data mapBogor, di mana setiap area/poligon akan diberi gradasi warna sesuai nilai pada kolom JumlahPenduduk.

p <- ggplot() +  
  geom_sf(data=mapBogor, aes(fill=JumlahPenduduk))
p

ggplot2 dilengkapi dengan banyak opsi untuk mengatur tampilkan dari output. Misalnya scale_fill_gradientn() untuk mengatur gradasi warna (secara otomatis ataupun costum), labs() untuk menambahkan judul grafik maupun keterangan sumbu, theme() untuk mengatur tema (legenda, jenis dan ukuran huruf, dan lain-lain), scale_**_continuous() pengaturan sumbu tegak maupun sumbu mendatar.

colorPalette = RColorBrewer::brewer.pal(5,"YlGnBu")
legendBreak = c(120,170,230)*1000
yBreak = seq(106.72, 106.86, by=0.04)

p + scale_fill_gradientn(colors = colorPalette, 
                       breaks = legendBreak, 
                       name = "Jumlah Penduduk") +
  labs(title = "Jumlah Penduduk Kota Bogor")  +
  theme(legend.text = element_text(size=7),
        legend.title = element_text(size=7),
        axis.text.x = element_text(size = 7),
        axis.text.y = element_text(size = 7),
        title = element_text(size=12, face='bold')) +
  scale_x_continuous(breaks = yBreak) 

Peta Interaktif dengan leaflet

Selain secara statis, peta choropleth juga dapat ditampilkan secara dinamis atau interaktif. Biasanya jika peta yang dibuat akan ditampilkan dalam halaman web. Disebut interaktif karena pengguna tidak hanya bisa “melihat” peta saja, tapi juga bisa berinteraksi langsung seperti memilih layer yang akan ditampilkan, memperbesar atau memperkecil peta, menyorot bagian tertentu pada peta untuk menampilkan data atau statistik, dan lain-lain.

Leaflet adalah salah satu JavaScript library yang paling populer untuk membuat peta interaktif. Untuk yang tidak familiar dengan JavaScript, peta leaflet dapat dibuat dibuat, diatur dan diintegrasikan dengan R menggunakan paket leaflet (Cheng, Karambelkar, and Xie 2021). Untuk menginstal paket, dapat dilakukan dengan perintah berikut:

# opsional, jika belum terinstal
install.packages("leaflet")

Lalu panggil ke dalam working space.

library(leaflet)

Selanjutnya leaflet dapat dibuat melalui R console atau diintegrasikan dalam RMarkdown dan aplikasi Shinny dengan cara yang mirip dengan ggplot2.

Berikut perintah-perintah untuk menampilkan jumlah penduduk Kota Bogor dengan peta leaflet.

  1. leaflet(): inisiasi peta dengan memanggil fungsi leaflet()
  2. addProviderTiles(): menambahkan peta dasar (base map) dengan perintah
  3. addPolygons(): menabahkan poligon dengan gradasi warna berdasarkan jumlah penduduk. Pengaturan warna gradasi menggunakan colorNumeric(). Ditambahkan pula opsi label untu menampilkan popup, yang akan muncul ketika pengguna menyorot area tertentu.
  4. addLegend(): menambahkan legenda
  5. addLayersControl(): menampilkan tombol untuk memilih layer yang akan ditampilkan
  6. setView(): mengatur posisi dan zooming default
# membuat custom palette warna
populationPalette <- colorNumeric(
  palette = "YlGnBu",
  domain = mapBogor$JumlahPenduduk
)

# membuat custom popup
popupLabel <- paste0(
    "<b>Kecamatan ", mapBogor$Kecamatan,"</b><br/>", 
    "Jumlah Penduduk (jiwa): ", mapBogor$JumlahPenduduk, "<br/>", 
    "Luas Wilayah (km2): ", mapBogor$LuasWilayah, "<br/>", 
    "Kepadatan Penduduk (jiwa/km2): ", mapBogor$KepadatanPenduduk) %>%
  lapply(htmltools::HTML)
# membuat peta leaflet
leaflet(mapBogor) %>% 
  addProviderTiles(providers$CartoDB.PositronNoLabels, group = "Light Mode") %>%
  addProviderTiles(providers$CartoDB.DarkMatterNoLabels, group = "Dark Mode") %>%
  
  addPolygons(weight = 1,
              opacity = 1, 
              fillOpacity = 0.9,
              label = popupLabel,
              color = ~populationPalette(JumlahPenduduk),
              highlightOptions = highlightOptions(color = "white", 
                                                  weight = 2, 
                                                  bringToFront = TRUE) ) %>%
  addLegend(position = "bottomright", 
            pal = populationPalette, 
            values = ~JumlahPenduduk,
            title = "Jumlah\nPenduduk",
            opacity = 1) %>%
  
  addLayersControl(position = 'topright',
                   baseGroups = c("Light Mode", "Dark Mode"),
                   options = layersControlOptions(collapsed = FALSE)) %>%
  
  setView(lat = - 6.595, lng = 106.87, zoom = 12)

Output peta di atas dapat diperbesar atau diperkecil, dan jika pengguna menyorot area tertentu maka akan muncul beberapa data dalam bentuk pop-up. Juga dapat memilih basemap yang akan digunakan, apakah terang atau gelap.

Sebetulnya peta ini masih bisa dikembangkan, misalnya dengan menambahkan data lain seperti kepadatan penduduk, jumlah penduduk menurut jenis kelamin dan data-data lainnya lalu pengguna diberi pilihan untuk memilih data apa yang ingin dilihatnya.

Referensi

Cheng, Joe, Bhaskar Karambelkar, and Yihui Xie. 2021. Leaflet: Create Interactive Web Maps with the JavaScript ’Leaflet’ Library. https://CRAN.R-project.org/package=leaflet.
HDX. 2020. “Indonesia - Subnational Administrative Boundaries,” April. https://data.humdata.org/dataset/indonesia-administrative-boundary-polygons-lines-and-places-levels-0-4b.
Kemendagri. 2020. “Visualisasi Data Kependudukan,” June. https://gis.dukcapil.kemendagri.go.id/peta/.
Müller, Kirill, Hadley Wickham, David A. James, and Seth Falcon. 2021. RSQLite: ’SQLite’ Interface for r. https://CRAN.R-project.org/package=RSQLite.
Pebesma, Edzer. 2018. Simple Features for R: Standardized Support for Spatial Vector Data.” The R Journal 10 (1): 439–46. https://doi.org/10.32614/RJ-2018-009.
R Special Interest Group on Databases (R-SIG-DB), Hadley Wickham, and Kirill Müller. 2021. DBI: R Database Interface. https://CRAN.R-project.org/package=DBI.
Rahma Anisa, Abdul Aziz Nurussadad, Gerry Alfa Dito. 2021. “Working with Spatial Data,” February. https://newlms.ipb.ac.id/course/view.php?id=4791.
Rahmi, Annisa. 2021. “Database Queries Dan Spatial Data,” March. https://www.rpubs.com/annisarahmi/732309.
Wickham, Hadley. 2016. Ggplot2: Elegant Graphics for Data Analysis. Springer-Verlag New York. https://ggplot2.tidyverse.org.
Wickham, Hadley, Mara Averick, Jennifer Bryan, Winston Chang, Lucy D’Agostino McGowan, Romain François, Garrett Grolemund, et al. 2019. “Welcome to the tidyverse.” Journal of Open Source Software 4 (43): 1686. https://doi.org/10.21105/joss.01686.
Wickham, Hadley, Romain François, Lionel Henry, and Kirill Müller. 2021. Dplyr: A Grammar of Data Manipulation. https://CRAN.R-project.org/package=dplyr.
Wickham, Hadley, and Garrett Grolemund. 2017. R for Data Science: Import, Tidy, Transform, Visualize, and Model Data. 1st ed. Paperback; O’Reilly Media. http://r4ds.had.co.nz/.

  1. Penulis adalah mahasiswa Pascasarjana Statistika dan Sains Data, Institut Pertanian Bogor, NIM: G1501211061, ↩︎