P3a-Database Queries with R

Database Queries

Beberapa package yang digunakan untuk bekerja dengan database menggunakan R software adalah:

  • DBI berfungsi memisahkan konektivitas ke DBMS menjadi “front-end” dan “back-end”. Aplikasi hanya menggunakan API front-end yang terbuka. Fasilitas back-end yang berkomunikasi dengan DBMS tertentu (SQLite, MySQL, PostgreSQL, MonetDB, dll.) disediakan oleh driver (package lain) yang dipanggil secara otomatis.

  • dplyr dan dbplyr memiliki SQL back-end yang digeneralisasi untuk bisa bekerja dengan database.

  • odbc menyediakan antarmuka yang sesuai dengan DBI untuk driver Open Database Connectivity (ODBC). Hal ini memungkinkan koneksi yang efisien dan mudah diatur ke database apa pun dengan driver ODBC yang tersedia, termasuk SQL Server, Oracle, MySQL, PostgreSQL, SQLite, dan lainnya.

Connection to a Database

Kali ini contoh pemanggilan database akan menggunakan contoh data Northwind . Untuk dapat melakukannya, package DBI dan RSQLite perlu di-install terlebih dulu.

install.packages(c("RSQLite", "DBI"), dependencies = TRUE)

Selanjutnya kita panggil package yang akan digunakan. Kita dapat memanggil package dplry dan dbplyr, atau bisa dengan hanya memanggil package tidyverse yang mencakup keduanya sekaligus. Selain itu, jangan lupa untuk memanggil package RSQLite dan DBI.

library(tidyverse)
## -- Attaching packages --------------------------------------- tidyverse 1.3.0 --
## v ggplot2 3.3.3     v purrr   0.3.4
## v tibble  3.0.6     v dplyr   1.0.4
## v tidyr   1.1.2     v stringr 1.4.0
## v readr   1.4.0     v forcats 0.5.1
## -- Conflicts ------------------------------------------ tidyverse_conflicts() --
## x dplyr::filter() masks stats::filter()
## x dplyr::lag()    masks stats::lag()
library(RSQLite)
library(DBI)

Secara umum, koneksi terhadap database pada R software adalah dengan menggunakan syntax berikut:

DBI::dbConnect(RSQLite::SQLite(), path = ":dbname:")

Fungsi dapat pula dijalankankan tanpa memanggil package dengan perintah library() atau require(), jika syntax yang ditulis menyertakan nama package tempat fungsi tersebut berasal. Penyertaan ini dengan menggunakan tanda ::.

Untuk menjalankan syntax berikut ini, Anda perlu mengunduh Database Northwind_large.sqlite dan menyimpannya pada C:/sqlite/db/

Northwind<-DBI::dbConnect(RSQLite::SQLite(), "C:/sqlite/db/Northwind_large.sqlite")

Dapat dilihat di syntax di atas, Anda tidak perlu menggunakan path=, karena C:/sqlite/db/Northwind_large.sqlite langsung didentifikasi langsung sebagai path. Anda akan mempelajari hal seperti ini di Mata Kuliah STA561 - Pemrograman Statistika khususnya di Pertemuan 6 - Pemrograman Fungsi R

class(Northwind)
## [1] "SQLiteConnection"
## attr(,"package")
## [1] "RSQLite"

Perhatikan bahwa syntax di atas tidak membaca data ke dalam R seperti halnya fungsi read_csv() melainkan berupa instruksi kepada R untuk mengkoneksikan database SQLite yang terdapat pada file Northwind_large.sqlite.

Selanjutnya, fungsi dbListTables dapat digunakan untuk melihat tables yang terdapat di dalam database.

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

Anda bisa melakukan hal yang sama hanya menggunakan syntax dbListTables(Northwind), karena di atas Kita telah memanggil package RSQLite dengan syntax library(RSQLite).

Untuk mengakses tabel pada database dapat digunakan fungsi tbl(database, "table") dari package dplyr. Misalkan, kita ingin melihat isi tabel Product dari database Northwind_large.sqlite. Perlu diperhatikan, Kita sudah memasukkan Northwind_large.sqlite sebagai objek Northwind

product<-dplyr::tbl(Northwind,"Product")
class(product)
## [1] "tbl_SQLiteConnection" "tbl_dbi"              "tbl_sql"             
## [4] "tbl_lazy"             "tbl"

Perhatikan bahwa product merupakan object dengan class tbl yang dapat diperlakukan mirip seperti data.frame.

Berikut adalah isi dari obyek product

product
## # Source:   table<Product> [?? x 10]
## # Database: sqlite 3.34.1 [C:\sqlite\db\Northwind_large.sqlite]
##       Id ProductName SupplierId CategoryId QuantityPerUnit UnitPrice
##    <int> <chr>            <int>      <int> <chr>               <dbl>
##  1     1 Chai                 1          1 10 boxes x 20 ~      18  
##  2     2 Chang                1          1 24 - 12 oz bot~      19  
##  3     3 Aniseed Sy~          1          2 12 - 550 ml bo~      10  
##  4     4 Chef Anton~          2          2 48 - 6 oz jars       22  
##  5     5 Chef Anton~          2          2 36 boxes             21.4
##  6     6 Grandma's ~          3          2 12 - 8 oz jars       25  
##  7     7 Uncle Bob'~          3          7 12 - 1 lb pkgs.      30  
##  8     8 Northwoods~          3          2 12 - 12 oz jars      40  
##  9     9 Mishi Kobe~          4          6 18 - 500 g pkg~      97  
## 10    10 Ikura                4          8 12 - 200 ml ja~      31  
## # ... with more rows, and 4 more variables: UnitsInStock <int>,
## #   UnitsOnOrder <int>, ReorderLevel <int>, Discontinued <int>

From dplyr to SQL

Seandainya kita ingin mengetahui minimum stok untuk produk dengan harga di atas $20 untuk setiap supplier. Untuk memperoleh informasi tersebut kita dapat menuliskan syntax berikut ini.

q<-product %>%
  filter(UnitPrice>20) %>% 
  group_by(SupplierId) %>% 
  summarize(stock=min(UnitsInStock)) %>%
  arrange(desc(stock))
q
## Warning: Missing values are always removed in SQL.
## Use `MIN(x, na.rm = TRUE)` to silence this warning
## This warning is displayed only once per session.
## # Source:     lazy query [?? x 2]
## # Database:   sqlite 3.34.1 [C:\sqlite\db\Northwind_large.sqlite]
## # Ordered by: desc(stock)
##    SupplierId stock
##         <int> <int>
##  1         25   115
##  2          9   104
##  3          8    40
##  4          6    35
##  5          4    29
##  6         15    26
##  7          5    22
##  8         26    21
##  9         28    19
## 10         29    17
## # ... with more rows
class(q)
## [1] "tbl_SQLiteConnection" "tbl_dbi"              "tbl_sql"             
## [4] "tbl_lazy"             "tbl"

Pada beberapa kasus, data yang digunakan bisa saja berukuran sangat besar sehingga lebih efisien jika disimpan dalam bentuk database. Syntax di atas merupakan contoh cara kita memproses data dalam bentuk database dengan cara menggunakan package dplyr untuk menghubungkan syntax pada R dengan data pada SQLite.

dplyr mampu menejermahkan pipeline dengan fungsi show_query() seperti contoh di bawah ini.

dplyr::show_query(q)
## <SQL>
## SELECT `SupplierId`, MIN(`UnitsInStock`) AS `stock`
## FROM `Product`
## WHERE (`UnitPrice` > 20.0)
## GROUP BY `SupplierId`
## ORDER BY `stock` DESC

Sebaliknya, kita dapat menuliskan fungsi sql dengan fungsi tbl dari package dplyr.

tbl(Northwind, sql("SELECT productname, categoryid, unitprice FROM Product ORDER BY productname LIMIT 10"))
## # Source:   SQL [?? x 3]
## # Database: sqlite 3.34.1 [C:\sqlite\db\Northwind_large.sqlite]
##    productname                  categoryid unitprice
##    <chr>                             <int>     <dbl>
##  1 Alice Mutton                          6      39  
##  2 Aniseed Syrup                         2      10  
##  3 Boston Crab Meat                      8      18.4
##  4 Camembert Pierrot                     4      34  
##  5 Carnarvon Tigers                      8      62.5
##  6 Chai                                  1      18  
##  7 Chang                                 1      19  
##  8 Chartreuse verte                      1      18  
##  9 Chef Anton's Cajun Seasoning          2      22  
## 10 Chef Anton's Gumbo Mix                2      21.4

Contoh lainnya, misalkan kita ingin melihat 10 perusahaan pertama dari table Customer.

tbl(Northwind,"Customer") %>%
  select(CompanyName) %>%
  head(n=10)
## # Source:   lazy query [?? x 1]
## # Database: sqlite 3.34.1 [C:\sqlite\db\Northwind_large.sqlite]
##    CompanyName                       
##    <chr>                             
##  1 Alfreds Futterkiste               
##  2 Ana Trujillo Emparedados y helados
##  3 Antonio Moreno Taquería           
##  4 Around the Horn                   
##  5 Berglunds snabbköp                
##  6 Blauer See Delikatessen           
##  7 Blondesddsl père et fils          
##  8 Bólido Comidas preparadas         
##  9 Bon app                           
## 10 Bottom-Dollar Markets

Combining Multiple Tables with dplyr in R

Untuk mempelajari mengenai penggabungan tables ini, kita akan menggunakan Datasets Band membership dari package dplyr. Datasets ini terdiri dari 3 buah tibble, dengan ukuran data yang kecil, yaitu hanya 3 baris dan 2 kolom.

data("band_members")
band_members
## # A tibble: 3 x 2
##   name  band   
##   <chr> <chr>  
## 1 Mick  Stones 
## 2 John  Beatles
## 3 Paul  Beatles
data("band_instruments")
band_instruments
## # A tibble: 3 x 2
##   name  plays 
##   <chr> <chr> 
## 1 John  guitar
## 2 Paul  bass  
## 3 Keith guitar
data("band_instruments2")
band_instruments2
## # A tibble: 3 x 2
##   artist plays 
##   <chr>  <chr> 
## 1 John   guitar
## 2 Paul   bass  
## 3 Keith  guitar

Agar diperhatikan, band_instruments dan band_instruments2 memiliki data yang sama, namun berbeda pada penamaan kolom nya, band_instruments menggunakan name, yang sama dengan nama kolom di band_members, sedangkan band_instruments2 menggunakan artist

Fungsi yang akan digunakan dalam penggabungan tables ini berasal dari package dplyr.

inner_join()

inner_join() menghasilkan semua baris pada table x yang memiliki kesamaan nilai dengan table y, dan semua kolom dari x dan y.

Misal, Anda hendak menggabungkan band_members dan band_instruments secara inner_join, maka Anda bisa menggunakan syntax berikut ini :

inner_join(band_members,band_instruments)
## Joining, by = "name"
## # A tibble: 2 x 3
##   name  band    plays 
##   <chr> <chr>   <chr> 
## 1 John  Beatles guitar
## 2 Paul  Beatles bass
band_members %>% inner_join(band_instruments)
## Joining, by = "name"
## # A tibble: 2 x 3
##   name  band    plays 
##   <chr> <chr>   <chr> 
## 1 John  Beatles guitar
## 2 Paul  Beatles bass

left_join()

left_join() menghasilkan semua baris pada table x, dan semua kolom pada x dan y, dimana untuk baris pada x yang tidak memiliki kesamaan dengan y diisi dengan nilai NApada kolom yang baru.

Misal, Anda hendak menggabungkan band_members dan band_instruments secara left_join, maka Anda bisa menggunakan syntax berikut ini :

left_join(band_members,band_instruments)
## Joining, by = "name"
## # A tibble: 3 x 3
##   name  band    plays 
##   <chr> <chr>   <chr> 
## 1 Mick  Stones  <NA>  
## 2 John  Beatles guitar
## 3 Paul  Beatles bass

right_join()

right_join() menghasilkan semua baris pada table y, dan semua kolom pada x dan y, dimana untuk baris pada y yang tidak memiliki kesamaan dengan x akan diisi dengan nilai NA pada kolom yang baru.

Misal, Anda hendak menggabungkan band_members dan band_instruments secara right_join`, maka Anda bisa menggunakan syntax berikut ini :

right_join(band_members,band_instruments)
## Joining, by = "name"
## # A tibble: 3 x 3
##   name  band    plays 
##   <chr> <chr>   <chr> 
## 1 John  Beatles guitar
## 2 Paul  Beatles bass  
## 3 Keith <NA>    guitar

full_join()

full_join() menghasilkan semua baris dan kolom dari x dan y. Jika ada nilai yang tidak sama (match) antara x dan y, maka akan bernilai NA.

Misal, Anda hendak menggabungkan band_members dan band_instruments secara full_join, maka Anda bisa menggunakan syntax berikut ini :

full_join(band_members,band_instruments)
## Joining, by = "name"
## # A tibble: 4 x 3
##   name  band    plays 
##   <chr> <chr>   <chr> 
## 1 Mick  Stones  <NA>  
## 2 John  Beatles guitar
## 3 Paul  Beatles bass  
## 4 Keith <NA>    guitar

semi_join()

semi_join() menghasilkan semua baris pada table x yang memiliki kesamaan nilai dengan table y, dan semua kolom dari x. Ini mirip dengan inner_join(), bedanya inner_join() mengembalikan semua kolom dari x dan y.

Misal, Anda hendak menggabungkan band_members dan band_instruments secara semi_join, maka Anda bisa menggunakan syntax berikut ini :

semi_join(band_members,band_instruments)
## Joining, by = "name"
## # A tibble: 2 x 2
##   name  band   
##   <chr> <chr>  
## 1 John  Beatles
## 2 Paul  Beatles

anti_join()

anti_join() menghasilkan semua baris dari x yang TIDAK memiliki kesamaan dengan y, dan semua kolom yang berasal dari x.

Misal, Anda hendak menggabungkan band_members dan band_instruments secara anti_join, maka Anda bisa menggunakan syntax berikut ini :

anti_join(band_members,band_instruments)
## Joining, by = "name"
## # A tibble: 1 x 2
##   name  band  
##   <chr> <chr> 
## 1 Mick  Stones

Catatan

Ada kalanya Anda perlu menggabungkan data seperti band_members dan band_instruments2 dimana tidak ada nama kolom yang sama, namun sebenarnya bisa digabungkan. Maka Anda memerlukan sedikit tambahan syntax.

Misal,Anda hendak menggabungkan band_members dan band_instruments2 secara full_join, maka Anda bisa menggunakan syntax berikut ini :

full_join(band_members, band_instruments2, by = c("name" = "artist"))
## # A tibble: 4 x 3
##   name  band    plays 
##   <chr> <chr>   <chr> 
## 1 Mick  Stones  <NA>  
## 2 John  Beatles guitar
## 3 Paul  Beatles bass  
## 4 Keith <NA>    guitar

Selengkapnya dapat dipelajari di sini

Referensi

Contributors Data Carpenters. (2020, August 12). SQL databases and R. Data Carpentry. Retrieved from https://datacarpentry.org/R-ecology-lesson/05-r-and-databases.html#Introduction

Wickham, H., & Ruiz, E. (n.d.). Introduction to dbplyr. dbplyr. Retrieved from https://dbplyr.tidyverse.org/


  1. IPB University, ↩︎

  2. IPB University, ↩︎

  3. Badan Informasi Geospasial, ↩︎