1 SETUP

library(tidyverse)
library(ggplot2)
library(forcats)
library(caret)
library(forecast)
library(TTR)
library(fpp)
library(xts)
library(lubridate)
library(padr)
library(MLmetrics)
options(scipen=999)
knitr::opts_chunk$set(fig.align = "center")
R.version
##                _                                
## platform       x86_64-w64-mingw32               
## arch           x86_64                           
## os             mingw32                          
## crt            ucrt                             
## system         x86_64, mingw32                  
## status                                          
## major          4                                
## minor          2.1                              
## year           2022                             
## month          06                               
## day            23                               
## svn rev        82513                            
## language       R                                
## version.string R version 4.2.1 (2022-06-23 ucrt)
## nickname       Funny-Looking Kid

LBB ini mendemonstrasikan pemodelan machine learning untuk data time series.

Github
Untuk file .Rmd atau styles.css bisa melihat github proyek ini di taruma/mls-lbb-5.
Notasi \(\text{matematika}\)
Untuk ribuan akan menggunakan pemisah koma (\(,\)), sedangkan pemisah untuk angka dibelakang koma (desimal/mantissa) menggunakan titik (\(.\)).
Catatan
Pada LBB ini untuk piping akan menggunakan native piping |> yang tersedia di R 4.1+ untuk versi selain itu gunakan %>%.

2 Dataset

Dataset yang digunakan adalah data Daily Exchange Rates per Euro 1999-2022 yang diperoleh dari Kaggle (Data diunduh pada hari Senin, 28 November 2022). Dataset ini berisikan Exchange Rate (Nilai tukar) Euro/EUR (€) terhadap mata uang negara lain setiap harinya sejak tahun 1999. Berikut 6 baris pertama dataset.

exchange <- read.csv("data/euro-daily-hist_1999_2022.csv")
exchange |> head()

Sebagai catatan, dataset diurutkan dari yang terbaru ke yang terlama. Sehingga, baris pertama merupakan data paling terbaru (2022) meski di halaman dataset dimulai dari 1999. Berikut 6 baris terakhir dataset exchange.

exchange |> tail()

Berikut struktur dataset menggunakan str().

str(exchange)
## 'data.frame':    6177 obs. of  41 variables:
##  $ Period.Unit.             : chr  "2022-11-15" "2022-11-14" "2022-11-11" "2022-11-10" ...
##  $ X.Australian.dollar..    : chr  "1.5415" "1.5427" "1.5459" "1.5525" ...
##  $ X.Bulgarian.lev..        : chr  "1.9558" "1.9558" "1.9558" "1.9558" ...
##  $ X.Brazilian.real..       : chr  "5.5480" "5.4605" "5.5147" "5.2860" ...
##  $ X.Canadian.dollar..      : chr  "1.3816" "1.3706" "1.3698" "1.3467" ...
##  $ X.Swiss.franc..          : chr  "0.9790" "0.9751" "0.9844" "0.9834" ...
##  $ X.Chinese.yuan.renminbi..: chr  "7.3299" "7.2906" "7.3267" "7.2184" ...
##  $ X.Cypriot.pound..        : chr  "" "" "" "" ...
##  $ X.Czech.koruna..         : chr  "24.326" "24.289" "24.278" "24.361" ...
##  $ X.Danish.krone..         : chr  "7.4388" "7.4382" "7.4384" "7.4381" ...
##  $ X.Estonian.kroon..       : chr  "" "" "" "" ...
##  $ X.UK.pound.sterling..    : chr  "0.87455" "0.87513" "0.87538" "0.87298" ...
##  $ X.Greek.drachma..        : chr  "" "" "" "" ...
##  $ X.Hong.Kong.dollar..     : chr  "8.1430" "8.0852" "8.0758" "7.8128" ...
##  $ X.Croatian.kuna..        : chr  "7.5459" "7.5465" "7.5445" "7.5427" ...
##  $ X.Hungarian.forint..     : chr  "405.45" "407.28" "402.08" "400.95" ...
##  $ X.Indonesian.rupiah..    : chr  "16164.78" "16052.12" "15979.45" "15615.60" ...
##  $ X.Israeli.shekel..       : chr  "3.5694" "3.5410" "3.5255" "3.5453" ...
##  $ X.Indian.rupee..         : chr  "84.1304" "83.7779" "83.2253" "81.3058" ...
##  $ X.Iceland.krona..        : num  150 150 149 148 147 ...
##  $ X.Japanese.yen..         : chr  "144.84" "144.86" "143.89" "145.47" ...
##  $ X.Korean.won..           : chr  "1365.61" "1369.32" "1359.20" "1373.96" ...
##  $ X.Lithuanian.litas..     : chr  "" "" "" "" ...
##  $ X.Latvian.lats..         : chr  "" "" "" "" ...
##  $ X.Maltese.lira..         : chr  "" "" "" "" ...
##  $ X.Mexican.peso..         : chr  "20.0795" "20.0985" "20.0239" "19.4562" ...
##  $ X.Malaysian.ringgit..    : chr  "4.7208" "4.7429" "4.7700" "4.6789" ...
##  $ X.Norwegian.krone..      : chr  "10.3570" "10.3143" "10.2635" "10.3615" ...
##  $ X.New.Zealand.dollar..   : chr  "1.6897" "1.6957" "1.7020" "1.6984" ...
##  $ X.Philippine.peso..      : chr  "59.532" "59.040" "59.106" "57.793" ...
##  $ X.Polish.zloty..         : chr  "4.7073" "4.6898" "4.6765" "4.7060" ...
##  $ X.Romanian.leu..         : num  4.91 4.9 4.89 4.89 4.9 ...
##  $ X.Russian.rouble..       : chr  "" "" "" "" ...
##  $ X.Swedish.krona..        : chr  "10.8081" "10.7713" "10.7241" "10.8743" ...
##  $ X.Singapore.dollar..     : chr  "1.4238" "1.4177" "1.4199" "1.3963" ...
##  $ X.Slovenian.tolar..      : chr  "" "" "" "" ...
##  $ X.Slovak.koruna..        : chr  "" "" "" "" ...
##  $ X.Thai.baht..            : chr  "36.9390" "36.9780" "37.0880" "36.7000" ...
##  $ X.Turkish.lira..         : num  19.4 19.2 19.1 18.5 18.7 ...
##  $ X.US.dollar..            : chr  "1.0404" "1.0319" "1.0308" "0.9954" ...
##  $ X.South.African.rand..   : chr  "17.8822" "17.8393" "17.7944" "17.6882" ...

Dari struktur diatas diketahui terdapat kolom/negara yang berupa character, yang seharusnya berupa angka/numeric. Oleh karena itu, dataset exchange harus diolah kembali sebelum dilakukan pemodelan. Sebagai catatan, dataset exchange memiliki \(6,177\) baris/observasi dan \(41\) kolom/variabel. Proses tersebut dilanjutkan ke R1 - Pemrosesan Data.

3 R1 - Pra-pemrosesan Data

Informasi struktur diatas menandakan banyak sekali yang harus diolah sebelum siap digunakan. Perubahan nama kolom dan tipe kolom merupakan hal yang cukup terlihat jelas dari kilasan dataset exchange. Sebelum memulai pra-pemrosesan data semua dataset yang digunakan pada tahap ini disimpan dalam objek exchange_r1.

exchange_r1 <- exchange
exchange_r1 |> head()

3.1 Menamai Kolom

Nama kolom di exchange bisa berbeda ketika pembacaan dataset menggunakan read_csv(), tapi untuk dataset ini digunakan read.csv(). Nama kolom disimpan dalam variabel name_columns.

name_columns <- colnames(exchange_r1)
name_columns
##  [1] "Period.Unit."              "X.Australian.dollar.."    
##  [3] "X.Bulgarian.lev.."         "X.Brazilian.real.."       
##  [5] "X.Canadian.dollar.."       "X.Swiss.franc.."          
##  [7] "X.Chinese.yuan.renminbi.." "X.Cypriot.pound.."        
##  [9] "X.Czech.koruna.."          "X.Danish.krone.."         
## [11] "X.Estonian.kroon.."        "X.UK.pound.sterling.."    
## [13] "X.Greek.drachma.."         "X.Hong.Kong.dollar.."     
## [15] "X.Croatian.kuna.."         "X.Hungarian.forint.."     
## [17] "X.Indonesian.rupiah.."     "X.Israeli.shekel.."       
## [19] "X.Indian.rupee.."          "X.Iceland.krona.."        
## [21] "X.Japanese.yen.."          "X.Korean.won.."           
## [23] "X.Lithuanian.litas.."      "X.Latvian.lats.."         
## [25] "X.Maltese.lira.."          "X.Mexican.peso.."         
## [27] "X.Malaysian.ringgit.."     "X.Norwegian.krone.."      
## [29] "X.New.Zealand.dollar.."    "X.Philippine.peso.."      
## [31] "X.Polish.zloty.."          "X.Romanian.leu.."         
## [33] "X.Russian.rouble.."        "X.Swedish.krona.."        
## [35] "X.Singapore.dollar.."      "X.Slovenian.tolar.."      
## [37] "X.Slovak.koruna.."         "X.Thai.baht.."            
## [39] "X.Turkish.lira.."          "X.US.dollar.."            
## [41] "X.South.African.rand.."

Strategi dalam membuat nama baru adalah seluruh nama kolom berhuruf kecil semua dengan pemisahnya menggunakan titik. Jadi karakter seperti X. dihilangkan, dan .. dihapus juga. Proses tersebut dilakukan menggunakan str_replace() dan str_to_lower(). Nama baru kolom disimpan dalam variabel newname_columns. Berikut nama baru kolom.

newname_columns <- name_columns |> 
  str_replace("^X\\.", "") |> 
  str_replace("\\.+$", "") |> 
  str_to_lower()
newname_columns
##  [1] "period.unit"           "australian.dollar"     "bulgarian.lev"        
##  [4] "brazilian.real"        "canadian.dollar"       "swiss.franc"          
##  [7] "chinese.yuan.renminbi" "cypriot.pound"         "czech.koruna"         
## [10] "danish.krone"          "estonian.kroon"        "uk.pound.sterling"    
## [13] "greek.drachma"         "hong.kong.dollar"      "croatian.kuna"        
## [16] "hungarian.forint"      "indonesian.rupiah"     "israeli.shekel"       
## [19] "indian.rupee"          "iceland.krona"         "japanese.yen"         
## [22] "korean.won"            "lithuanian.litas"      "latvian.lats"         
## [25] "maltese.lira"          "mexican.peso"          "malaysian.ringgit"    
## [28] "norwegian.krone"       "new.zealand.dollar"    "philippine.peso"      
## [31] "polish.zloty"          "romanian.leu"          "russian.rouble"       
## [34] "swedish.krona"         "singapore.dollar"      "slovenian.tolar"      
## [37] "slovak.koruna"         "thai.baht"             "turkish.lira"         
## [40] "us.dollar"             "south.african.rand"

Nama baru tersebut diterapkan ke dataset exchange_r1 dan disimpan dalam objek baru bernama exchange_r1_newname.

exchange_r1_newname <- exchange_r1 |> 
  setNames(newname_columns)

exchange_r1_newname |> head()

Dan berikut secara sekilas data exchange_r1_newname.

exchange_r1_newname |> 
  glimpse()
## Rows: 6,177
## Columns: 41
## $ period.unit           <chr> "2022-11-15", "2022-11-14", "2022-11-11", "2022-…
## $ australian.dollar     <chr> "1.5415", "1.5427", "1.5459", "1.5525", "1.5538"…
## $ bulgarian.lev         <chr> "1.9558", "1.9558", "1.9558", "1.9558", "1.9558"…
## $ brazilian.real        <chr> "5.5480", "5.4605", "5.5147", "5.2860", "5.1947"…
## $ canadian.dollar       <chr> "1.3816", "1.3706", "1.3698", "1.3467", "1.3501"…
## $ swiss.franc           <chr> "0.9790", "0.9751", "0.9844", "0.9834", "0.9880"…
## $ chinese.yuan.renminbi <chr> "7.3299", "7.2906", "7.3267", "7.2184", "7.2813"…
## $ cypriot.pound         <chr> "", "", "", "", "", "", "", "", "", "", "", "", …
## $ czech.koruna          <chr> "24.326", "24.289", "24.278", "24.361", "24.337"…
## $ danish.krone          <chr> "7.4388", "7.4382", "7.4384", "7.4381", "7.4382"…
## $ estonian.kroon        <chr> "", "", "", "", "", "", "", "", "", "", "", "", …
## $ uk.pound.sterling     <chr> "0.87455", "0.87513", "0.87538", "0.87298", "0.8…
## $ greek.drachma         <chr> "", "", "", "", "", "", "", "", "", "", "", "", …
## $ hong.kong.dollar      <chr> "8.1430", "8.0852", "8.0758", "7.8128", "7.8801"…
## $ croatian.kuna         <chr> "7.5459", "7.5465", "7.5445", "7.5427", "7.5425"…
## $ hungarian.forint      <chr> "405.45", "407.28", "402.08", "400.95", "403.53"…
## $ indonesian.rupiah     <chr> "16164.78", "16052.12", "15979.45", "15615.60", …
## $ israeli.shekel        <chr> "3.5694", "3.5410", "3.5255", "3.5453", "3.5621"…
## $ indian.rupee          <chr> "84.1304", "83.7779", "83.2253", "81.3058", "81.…
## $ iceland.krona         <dbl> 149.9, 150.3, 148.7, 147.5, 146.7, 146.3, 145.9,…
## $ japanese.yen          <chr> "144.84", "144.86", "143.89", "145.47", "146.82"…
## $ korean.won            <chr> "1365.61", "1369.32", "1359.20", "1373.96", "136…
## $ lithuanian.litas      <chr> "", "", "", "", "", "", "", "", "", "", "", "", …
## $ latvian.lats          <chr> "", "", "", "", "", "", "", "", "", "", "", "", …
## $ maltese.lira          <chr> "", "", "", "", "", "", "", "", "", "", "", "", …
## $ mexican.peso          <chr> "20.0795", "20.0985", "20.0239", "19.4562", "19.…
## $ malaysian.ringgit     <chr> "4.7208", "4.7429", "4.7700", "4.6789", "4.7098"…
## $ norwegian.krone       <chr> "10.3570", "10.3143", "10.2635", "10.3615", "10.…
## $ new.zealand.dollar    <chr> "1.6897", "1.6957", "1.7020", "1.6984", "1.7033"…
## $ philippine.peso       <chr> "59.532", "59.040", "59.106", "57.793", "58.236"…
## $ polish.zloty          <chr> "4.7073", "4.6898", "4.6765", "4.7060", "4.7010"…
## $ romanian.leu          <dbl> 4.9116, 4.9043, 4.8940, 4.8913, 4.9045, 4.8978, …
## $ russian.rouble        <chr> "", "", "", "", "", "", "", "", "", "", "", "", …
## $ swedish.krona         <chr> "10.8081", "10.7713", "10.7241", "10.8743", "10.…
## $ singapore.dollar      <chr> "1.4238", "1.4177", "1.4199", "1.3963", "1.4061"…
## $ slovenian.tolar       <chr> "", "", "", "", "", "", "", "", "", "", "", "", …
## $ slovak.koruna         <chr> "", "", "", "", "", "", "", "", "", "", "", "", …
## $ thai.baht             <chr> "36.9390", "36.9780", "37.0880", "36.7000", "36.…
## $ turkish.lira          <dbl> 19.3608, 19.1923, 19.0987, 18.5100, 18.6728, 18.…
## $ us.dollar             <chr> "1.0404", "1.0319", "1.0308", "0.9954", "1.0039"…
## $ south.african.rand    <chr> "17.8822", "17.8393", "17.7944", "17.6882", "17.…

Setelah nama kolom sudah dibersihkan, bisa dilanjutkan untuk mengubah tipe data setiap kolomnya.

3.2 Mengubah Tipe Data Kolom

Melihat informasi sekilas mengenai dataset exchange_r1_newname. Terdapat dua jenis data yang berbeda di dataset. Kolom pertama yaitu period.unit berupa tanggal dengan format YYYY-MM-DD. Sedangkan kolom lainnya, merupakan kolom numerik.

3.2.1 Kolom Waktu

Kolom yang merupakan waktu yaitu period.unit. Berikut sekilas data pada kolom periode.unit.

set.seed(41608481)
exchange_r1_newname$period.unit |> sample(size=10)
##  [1] "2021-12-30" "1999-10-13" "2013-07-15" "2001-12-14" "2019-07-25"
##  [6] "2015-02-16" "2009-04-30" "2007-05-10" "2012-03-09" "2004-10-05"

Setelah mengetahui format YYYY-MM-DD, kolom tersebut bisa dikonversi menggunakan library lubridate dengan fungsi ymd(). Hasil perubahan tersebut disimpan dalam bentuk objek baru bernama exchange_r1_date. Berikut beberapa baris pertamanya.

exchange_r1_date <- exchange_r1_newname |> 
  mutate(period.unit = ymd(period.unit))
exchange_r1_date |> head()

Untuk memudahkan membaca, dataset diurutkan berdasarkan tanggal.

exchange_r1_date <- exchange_r1_date |> 
  arrange(period.unit)
exchange_r1_date |> head()

Kita lihat range waktu yang digunakan di exchange_r1_date dengan summary() pada kolom period.unit.

summary(exchange_r1_date$period.unit)
##         Min.      1st Qu.       Median         Mean      3rd Qu.         Max. 
## "1999-01-04" "2004-12-03" "2010-11-04" "2010-11-20" "2016-11-04" "2022-11-15"

Setelah mengetahui tanggal dimulainya dan berakhirnya dataset. Akan dicek apakah ada tanggal yang tidak terekam di dataset. Digunakan seq.Date() untuk membuat range dari 4 Januari 1999 hingga 15 November 2022.

date_start <- exchange_r1_date$period.unit |> min()
date_end <- exchange_r1_date$period.unit |> max()
date_range <- seq.Date(date_start, date_end, by = "day")
date_log_exist <- date_range %in% exchange_r1_date$period.unit
any(!date_log_exist)
## [1] TRUE

Ternyata dari koding diatas diketahui bahwa terdapat tanggal yang hilang. Berikut tanggal dimana observasi tidak tersedia.

date_missing <- date_range[!date_log_exist]
date_range_wday <- date_range |> wday(label=TRUE, abbr = FALSE) |> summary()
date_missing_wday <- date_missing |> wday(label=TRUE, abbr = FALSE) |> summary() 
data.frame(date_range_wday, date_missing_wday) |> 
  mutate(missing_date_percent = date_missing_wday / date_range_wday * 100)

Diketahui bahwa data pada hari Sabtu dan Minggu tidak direkam sama sekali. Sedangkan terdapat juga di hari lain yang hilang dalam dataset. Perlu diingat, hilangnya data ini fokus pada tanggal yang tidak tersedia pada dataset original dan bukan kehilangan data pada kolom lainnya.

Tanggal yang hilang pada exchange_r1_date dilengkapi menggunakan fungsi pad(). Berikut prosesnya dan pemeriksaannya setelah menggunakan fungsi pad() dan disimpan dalam objek baru bernama exchange_r1_date_complete.

exchange_r1_date_complete <- exchange_r1_date |> 
  pad(interval = "day")

all(date_range %in% exchange_r1_date_complete$period.unit)
## [1] TRUE

Dari nilai TRUE diatas, menandakan bahwa setiap tanggal pada range 4 Januari 1999 hingga 15 November 2022 sudah terdapat baris/observasinya di objek exchange_r1_date_complete. Dengan kata lain, tidak ada tanggal yang hilang dalam objek tersebut. Berikut 8 baris pertama dataset.

exchange_r1_date_complete |> head(8)

Untuk sementara padding yang dilakukan diisi dengan nilai NA, yang nanti akan diproses lebih lanjut sebelum masuk eksplorasi data. Dan berikut informasi mengenai perubahan/penambahan baris setelah padding.

data.frame(
  info = c("baris/observasi", "kolom/variabel"),
  nrow.date.original = exchange_r1 |> dim(),
  nrow.date.complete = exchange_r1_date_complete |> dim()
) |> column_to_rownames("info") |> 
  mutate(
    diff = nrow.date.complete - nrow.date.original,
    diff_percent = diff / nrow.date.complete * 100
  )

Dari informasi diatas diketahui bahwa banyaknya baris yang ditambahkan menggunakan padding sebanyak \(2,540\) baris atau sekitar \(29\%\) dari total baris lengkapnya data. Untuk catatan banyaknya baris tersebut disimpan dalam variabel total_padding.

total_padding <- nrow(exchange_r1_date_complete) - nrow(exchange_r1)

3.2.2 Kolom Numerik

Selain kolom period.unit, kolom lainnya merupakan kolom numerik. Akan tetapi pada saat pembacaan read.csv() terjadi coercing sehingga kolom tersebut berubah menjadi tipe data character. Sebelum memaksakan kolom tersebut menjadi numerik, diinginkan untuk mengidentifikasi apa yang terjadi dengan dataset tersebut sehingga membuat kolom tersebut menjadi character.

Berikut jumlah jenis kolom masing-masing dari dataset exchange_r1_date_complete.

exchange_r1_date_complete |> 
  sapply(class) |> table()
## 
## character      Date   numeric 
##        37         1         3

Diketahui ada \(37\) kolom yang bertipe character dan hanya \(3\) yang numeric. Berikut informasi mengenai tipe setiap kolomnya.

column_type_exchange <- exchange_r1_date_complete |> 
  sapply(class) |> as.data.frame() |> set_names("column.type") |> 
  arrange(column.type)
column_type_exchange

Berikut kolom yang berupa numerik dan tanggal.

column_type_exchange |> 
  filter(column.type %in% c("Date", "numeric"))

Jadi untuk iceland.krona, romanian.leu, dan turkish.lira sudah berupa numerik.

3.2.2.1 Investigasi coercion

Pada bagian ini, diinginkan untuk memperoleh informasi kenapa terjadinya coercion sehingga mengubah kolom tersebut menjadi tipe character. Untuk evaluasi tersebut akan menggunakan objek exchange_r1_coercion yang merupakan exchange_r1_date_complete dengan nama baris period.unit.

exchange_r1_coercion <- exchange_r1_date_complete |>
  column_to_rownames("period.unit")
  
exchange_r1_coercion |> head()

Berikut daftar karakter yang bukan merupakan angka di dataset exchange_r1_coercion.

list_of_character <- exchange_r1_coercion |>
  select_if(is_character) |> 
  lapply(
    function (.) {
      is_digits <- str_detect(., "[:digit:]")
      is_na <- is.na(.)
      not_digits <- !(is_digits | is_na)
      row_not_digits <- .[not_digits]
      row_unique <- row_not_digits |> unique()
    }
  )

list_of_character |> unlist() |> unique()
## [1] "-" ""

Dari informasi diatas, karakter "-" merupakan penyebab kenapa kolom tersebut diubah menjadi character. karakter "" (karakter kosong) muncul dikarenakan terdapat karakter dikolom tersebut dan terjadi coercion sehingga memaksakan nilai yang kosong menjadi "" (character)). Berikut dikolom mana saja karakter tersebut muncul.

list_of_character |> 
  lapply(\(.) {paste("[", ., "]", sep = "", collapse = ",")}) |> 
  as.data.frame() |> t() |> as.data.frame() |> setNames("not.digit")

Dari informasi diatas, bisa diketahui juga bahwa kolom yang tidak memiliki [] ($not.digit hanya memiliki nilai [-]) diartikan tidak memiliki data yang kosong saat pembacaan. Untuk menghitung tepat jumlah baris yang diisi dengan "-", elemen yang "" akan dibuat NA.

exchange_r1_coercion[exchange_r1_coercion == ""] <- NA
exchange_r1_coercion |> head()

Dengan kode diatas maka nilai yang "" akan diubah menjadi NA, sehingga karakter yang diidentifikasi penyebab coercion itu adalah "-". Selanjutnya ingin mengidentifikasi tanggal mana saja yang terjadi tidak ada data (menggunakan indikator "-").

func_logi_dash <- function(.) {
  is_dash <- (. == "-")
  logi_dash <- is_dash |> 
    apply(1, any, na.rm = TRUE)
}

logi_dash <- exchange_r1_coercion |> func_logi_dash()
exchange_r1_coercion |> filter(logi_dash)

Dari informasi diatas, terdapat \(63\) baris yang memiliki nilai "-" (tidak ada). Berikut hari dimana data tersebut tidak tersedia.

exchange_r1_coercion |> filter(logi_dash) |> 
  rownames() |> wday(label = TRUE, abbr = FALSE) |> table()
## 
##    Sunday    Monday   Tuesday Wednesday  Thursday    Friday  Saturday 
##         0        22         9         5         7        20         0

Perlu diingat bahwa hari Sabtu dan Minggu, memang tidak direkam dan tersedia di dataset original sama sekali, sehingga tidak muncul sama sekali di dataset. Langkah berikutnya adalah mengubah nilai "-" menjadi NA. Berikut hasil perubahannya di objek exchange_r1_coercion.

exchange_r1_coercion[exchange_r1_coercion == "-"] <- NA
exchange_r1_coercion |> subset(logi_dash)

Untuk memastikan tidak ada karakter yang bukan angka, diperiksa kembali objek exchange_r1_coercion. Berikut hasil pemeriksaannya.

exchange_r1_coercion |> 
  lapply(str_detect, pattern = "[:digit:]") |> 
  lapply(all, na.rm = TRUE) |> 
  unlist() |> all()
## [1] TRUE

Dari hasil diatas, sudah dipastikan bahwa pada exchange_r1_coercion seluruh isiannya merupakan angka. Oleh karena itu, semua kolom karakter tersebut bisa diubah menjadi numeric dengan fungsi as.numeric(). Hasil perubahan tersebut disimpan dalam objek baru bernama exchange_r1_numeric.

exchange_r1_numeric <- exchange_r1_coercion |> 
  mutate_if(is_character, as.numeric)
exchange_r1_numeric |> str()
## 'data.frame':    8717 obs. of  40 variables:
##  $ australian.dollar    : num  1.91 1.89 1.88 1.85 1.84 ...
##  $ bulgarian.lev        : num  NA NA NA NA NA NA NA NA NA NA ...
##  $ brazilian.real       : num  NA NA NA NA NA NA NA NA NA NA ...
##  $ canadian.dollar      : num  1.8 1.8 1.77 1.76 1.76 ...
##  $ swiss.franc          : num  1.62 1.61 1.61 1.62 1.61 ...
##  $ chinese.yuan.renminbi: num  NA NA NA NA NA NA NA NA NA NA ...
##  $ cypriot.pound        : num  0.582 0.582 0.582 0.582 0.582 ...
##  $ czech.koruna         : num  35.1 34.9 34.9 34.9 34.9 ...
##  $ danish.krone         : num  7.45 7.45 7.45 7.44 7.44 ...
##  $ estonian.kroon       : num  15.6 15.6 15.6 15.6 15.6 ...
##  $ uk.pound.sterling    : num  0.711 0.712 0.708 0.706 0.709 ...
##  $ greek.drachma        : num  327 325 325 324 324 ...
##  $ hong.kong.dollar     : num  9.13 9.13 9.1 9.01 9.03 ...
##  $ croatian.kuna        : num  NA NA NA NA NA NA NA NA NA NA ...
##  $ hungarian.forint     : num  251 251 251 250 250 ...
##  $ indonesian.rupiah    : num  9434 9315 9338 9219 9322 ...
##  $ israeli.shekel       : num  NA NA NA NA NA NA NA NA NA NA ...
##  $ indian.rupee         : num  NA NA NA NA NA NA NA NA NA NA ...
##  $ iceland.krona        : num  81.5 81.5 81.5 81.1 81 ...
##  $ japanese.yen         : num  134 131 131 129 130 ...
##  $ korean.won           : num  1399 1373 1360 1337 1367 ...
##  $ lithuanian.litas     : num  4.72 4.72 4.7 4.65 4.66 ...
##  $ latvian.lats         : num  0.667 0.666 0.665 0.663 0.665 ...
##  $ maltese.lira         : num  0.443 0.443 0.442 0.441 0.442 ...
##  $ mexican.peso         : num  11.6 11.6 11.5 11.6 11.4 ...
##  $ malaysian.ringgit    : num  4.48 4.48 4.46 4.42 4.43 ...
##  $ norwegian.krone      : num  8.86 8.77 8.73 8.63 8.59 ...
##  $ new.zealand.dollar   : num  2.22 2.2 2.19 2.15 2.16 ...
##  $ philippine.peso      : num  45.5 44.7 44.9 44.4 44.3 ...
##  $ polish.zloty         : num  4.07 4.02 4.01 4.02 4.04 ...
##  $ romanian.leu         : num  1.31 1.32 1.32 1.31 1.31 ...
##  $ russian.rouble       : num  25.3 26.6 27.4 27 27.2 ...
##  $ swedish.krona        : num  9.47 9.4 9.3 9.18 9.16 ...
##  $ singapore.dollar     : num  1.96 1.97 1.97 1.94 1.95 ...
##  $ slovenian.tolar      : num  189 189 189 189 189 ...
##  $ slovak.koruna        : num  43 42.8 42.8 42.8 42.6 ...
##  $ thai.baht            : num  42.7 42.5 42.7 42.2 42.6 ...
##  $ turkish.lira         : num  0.372 0.373 0.372 0.37 0.372 ...
##  $ us.dollar            : num  1.18 1.18 1.17 1.16 1.17 ...
##  $ south.african.rand   : num  6.94 6.8 6.73 6.83 6.79 ...

Proses berikutnya adalah mengevaluasi data yang hilang (NA).

3.3 Data yang hilang

Sebelum mengisi data yang hilang, sebaiknya di eksplorasi terlebih dahulu mengenai informasi hilangnya data. Dataset yang digunakan adalah objek exchange_r1_numeric yang telah mengubah seluruh tipe kolom menjadi numerik.

3.3.1 Eksplorasi data yang hilang

missing_val <- exchange_r1_numeric |> is.na() |> colSums()
missing_val |> as.data.frame() |> 
  mutate(missing_percentage = missing_val / nrow(exchange_r1_numeric) * 100) |> 
  arrange(missing_percentage |> desc())

Dari informasi diatas nilai NA yang dibangkitkan dari padding terhitung juga. Karena jumlah padding yang dilakukan sudah diketahui pada tahap sebelumnya yaitu sebanyak \(2,540\) baris (disimpan dalam variabel total_padding). Maka baris yang hilang dikurangi jumalh tersebut untuk memastikan banyaknya data yang hilang ketika tidak dilakukan padding atau hilangnya data aktualnya.

missing_val_actual <- missing_val |> as.data.frame() |> 
  mutate(
    missing_val_actual = missing_val - total_padding,
    percentage_missing_actual = missing_val_actual / (nrow(exchange_r1_numeric) - total_padding) * 100
  ) |> 
  arrange(percentage_missing_actual |> desc())
missing_val_actual

Berikut visualisasinya agar memudahkan melihat perbandingannya.

missing_val_actual |> 
  rownames_to_column("currency") |> 
  ggplot(aes(x = fct_reorder(currency, missing_val_actual), y = missing_val_actual)) +
  geom_col(aes(fill = percentage_missing_actual)) +
  scale_fill_gradient(low = "green", high = "black") +
  theme_bw() +
  labs(
    title = "Hilangnya Data (Aktual)",
    x = "Mata Uang",
    y = "Jumlah Baris yang Hilang",
    fill = "Persen Hilang"
  ) +
  coord_flip()
Banyaknya Baris yang Hilang (Aktual)

Banyaknya Baris yang Hilang (Aktual)

Dari informasi diatas diambil kesimpulan untuk mengambil beberapa kolom saja yang memiliki kelengkapan data yang lebih baik. Dari eksplorasi diatas, diambil keputusan untuk mengambil kolom yang memiliki data yang hilang aktual kurang dari \(100\). Kolom terpilih disimpan dalam variabel selected_country.

selected_country <- missing_val_actual |> 
  filter(missing_val_actual <= 100) |> 
  rownames()
selected_country
##  [1] "australian.dollar"  "canadian.dollar"    "swiss.franc"       
##  [4] "czech.koruna"       "danish.krone"       "uk.pound.sterling" 
##  [7] "hong.kong.dollar"   "hungarian.forint"   "indonesian.rupiah" 
## [10] "japanese.yen"       "korean.won"         "mexican.peso"      
## [13] "malaysian.ringgit"  "norwegian.krone"    "new.zealand.dollar"
## [16] "philippine.peso"    "polish.zloty"       "romanian.leu"      
## [19] "swedish.krona"      "singapore.dollar"   "thai.baht"         
## [22] "turkish.lira"       "us.dollar"          "south.african.rand"

Kolom-kolom yang terpilih disimpan dalam bentuk objek baru bernama exchange_r1_selected. Berikut kilasan dataset.

exchange_r1_selected <- exchange_r1_numeric |> 
  select(all_of(selected_country))
exchange_r1_selected |> glimpse()
## Rows: 8,717
## Columns: 24
## $ australian.dollar  <dbl> 1.9100, 1.8944, 1.8820, 1.8474, 1.8406, NA, NA, 1.8…
## $ canadian.dollar    <dbl> 1.8004, 1.7965, 1.7711, 1.7602, 1.7643, NA, NA, 1.7…
## $ swiss.franc        <dbl> 1.6168, 1.6123, 1.6116, 1.6165, 1.6138, NA, NA, 1.6…
## $ czech.koruna       <dbl> 35.107, 34.917, 34.850, 34.886, 34.938, NA, NA, 35.…
## $ danish.krone       <dbl> 7.4501, 7.4495, 7.4452, 7.4431, 7.4433, NA, NA, 7.4…
## $ uk.pound.sterling  <dbl> 0.71110, 0.71220, 0.70760, 0.70585, 0.70940, NA, NA…
## $ hong.kong.dollar   <dbl> 9.1332, 9.1341, 9.1010, 9.0131, 9.0302, NA, NA, 8.9…
## $ hungarian.forint   <dbl> 251.48, 250.80, 250.67, 250.09, 250.15, NA, NA, 249…
## $ indonesian.rupiah  <dbl> 9433.61, 9314.51, 9337.68, 9218.77, 9321.63, NA, NA…
## $ japanese.yen       <dbl> 133.73, 130.96, 131.42, 129.43, 130.09, NA, NA, 126…
## $ korean.won         <dbl> 1398.59, 1373.01, 1359.54, 1337.16, 1366.73, NA, NA…
## $ mexican.peso       <dbl> 11.6446, 11.5960, 11.4705, 11.5511, 11.4414, NA, NA…
## $ malaysian.ringgit  <dbl> 4.4798, 4.4805, 4.4637, 4.4203, 4.4295, NA, NA, 4.3…
## $ norwegian.krone    <dbl> 8.8550, 8.7745, 8.7335, 8.6295, 8.5900, NA, NA, 8.5…
## $ new.zealand.dollar <dbl> 2.2229, 2.2011, 2.1890, 2.1531, 2.1557, NA, NA, 2.1…
## $ philippine.peso    <dbl> 45.510, 44.745, 44.872, 44.436, 44.295, NA, NA, 43.…
## $ polish.zloty       <dbl> 4.0712, 4.0245, 4.0065, 4.0165, 4.0363, NA, NA, 4.0…
## $ romanian.leu       <dbl> 1.3111, 1.3168, 1.3168, 1.3092, 1.3143, NA, NA, 1.2…
## $ swedish.krona      <dbl> 9.4696, 9.4025, 9.3050, 9.1800, 9.1650, NA, NA, 9.0…
## $ singapore.dollar   <dbl> 1.9554, 1.9655, 1.9699, 1.9436, 1.9537, NA, NA, 1.9…
## $ thai.baht          <dbl> 42.6799, 42.5048, 42.6949, 42.1678, 42.5590, NA, NA…
## $ turkish.lira       <dbl> 0.3723, 0.3728, 0.3722, 0.3701, 0.3718, NA, NA, 0.3…
## $ us.dollar          <dbl> 1.1789, 1.1790, 1.1743, 1.1632, 1.1659, NA, NA, 1.1…
## $ south.african.rand <dbl> 6.9358, 6.7975, 6.7307, 6.8283, 6.7855, NA, NA, 6.7…

Dari dataset tersebut bisa dilanjutkan ke pengisian data yang hilang.

3.3.2 Pengisian data yang hilang

Dataset exchange_r1_selected diperiksa nilai data yang hilangnya. Dari narasi diatas diketahui, hari Sabtu dan Minggu, memang tidak tersedia datanya. Sehingga, observasi tersebut harus diisi jika menginginkan dataset yang kontinyu dengan interval waktu dalam bentuk harian. Berikut baris yang hilang dan disimpan dalam bentuk data.frame. Dan berikut jumlah hari yang hilang setiap weekday.

exchange_r1_missing <- exchange_r1_selected |> 
  filter(exchange_r1_selected |> is.na() |> apply(1, all)) |> 
  rownames_to_column("period.unit") |> 
  mutate(
    period.unit = ymd(period.unit)
  )
date_missing <- exchange_r1_missing$period.unit
date_missing |> wday(label = TRUE, abbr = FALSE) |> table()
## 
##    Sunday    Monday   Tuesday Wednesday  Thursday    Friday  Saturday 
##      1245        36        15        13        12        36      1245

Data yang hilang akan diisi dengan metode na.fill() dengan menggunakan fill = "extend". Data yang telah diisi disimpan pada objek exchange_r1_fill. Hal ini dipilih dikarenakan nilai mata uang bersifat kontinyu dari sebelum dan sesudahnya. Sehingga pengisian nilai harus diisi berdasarkan nilai terdekatnya.

exchange_r1_fill <- exchange_r1_selected |> 
  mutate_all(na.fill, fill = "extend")
exchange_r1_fill |> glimpse()
## Rows: 8,717
## Columns: 24
## $ australian.dollar  <dbl> 1.910000, 1.894400, 1.882000, 1.847400, 1.840600, 1…
## $ canadian.dollar    <dbl> 1.800400, 1.796500, 1.771100, 1.760200, 1.764300, 1…
## $ swiss.franc        <dbl> 1.616800, 1.612300, 1.611600, 1.616500, 1.613800, 1…
## $ czech.koruna       <dbl> 35.10700, 34.91700, 34.85000, 34.88600, 34.93800, 3…
## $ danish.krone       <dbl> 7.450100, 7.449500, 7.445200, 7.443100, 7.443300, 7…
## $ uk.pound.sterling  <dbl> 0.7111000, 0.7122000, 0.7076000, 0.7058500, 0.70940…
## $ hong.kong.dollar   <dbl> 9.133200, 9.134100, 9.101000, 9.013100, 9.030200, 9…
## $ hungarian.forint   <dbl> 251.4800, 250.8000, 250.6700, 250.0900, 250.1500, 2…
## $ indonesian.rupiah  <dbl> 9433.610, 9314.510, 9337.680, 9218.770, 9321.630, 9…
## $ japanese.yen       <dbl> 133.7300, 130.9600, 131.4200, 129.4300, 130.0900, 1…
## $ korean.won         <dbl> 1398.590, 1373.010, 1359.540, 1337.160, 1366.730, 1…
## $ mexican.peso       <dbl> 11.64460, 11.59600, 11.47050, 11.55110, 11.44140, 1…
## $ malaysian.ringgit  <dbl> 4.479800, 4.480500, 4.463700, 4.420300, 4.429500, 4…
## $ norwegian.krone    <dbl> 8.855000, 8.774500, 8.733500, 8.629500, 8.590000, 8…
## $ new.zealand.dollar <dbl> 2.222900, 2.201100, 2.189000, 2.153100, 2.155700, 2…
## $ philippine.peso    <dbl> 45.51000, 44.74500, 44.87200, 44.43600, 44.29500, 4…
## $ polish.zloty       <dbl> 4.071200, 4.024500, 4.006500, 4.016500, 4.036300, 4…
## $ romanian.leu       <dbl> 1.311100, 1.316800, 1.316800, 1.309200, 1.314300, 1…
## $ swedish.krona      <dbl> 9.469600, 9.402500, 9.305000, 9.180000, 9.165000, 9…
## $ singapore.dollar   <dbl> 1.955400, 1.965500, 1.969900, 1.943600, 1.953700, 1…
## $ thai.baht          <dbl> 42.67990, 42.50480, 42.69490, 42.16780, 42.55900, 4…
## $ turkish.lira       <dbl> 0.3723000, 0.3728000, 0.3722000, 0.3701000, 0.37180…
## $ us.dollar          <dbl> 1.178900, 1.179000, 1.174300, 1.163200, 1.165900, 1…
## $ south.african.rand <dbl> 6.935800, 6.797500, 6.730700, 6.828300, 6.785500, 6…

Dilakukan pengecekan ulang dengan anyNA().

anyNA(exchange_r1_fill)
## [1] FALSE

Menghasilkan FALSE yang artinya data sudah lengkap (tidak terdapat data yang kosong/hilang). Setelah ini dataset exchange_r1_fill, bisa dilanjutkan ke tahap R2 - Eksplorasi Data.


4 R2 - Eksplorasi Data

Setelah memastikan dataset sudah lengkap dan memenuhi kriteria kelengkapan data, dilanjutkan dengan mengeksplorasi dataset ini. Eksplorasi ini bisa berupa statistik deskriptif ataupun analisis terkait jenis data deret waktu (time series). Pada tahap ini dataset disimpan sebagai objek baru bernama exchange_r2 yang merupakan objek exchange_r1_fill. Kolom period.unit dimunculkan kembali dari nama baris dan diubah sebagai jenis data waktu.

exchange_r2 <- exchange_r1_fill |> 
  rownames_to_column("period.unit") |> 
  mutate(period.unit = ymd(period.unit))
exchange_r2 |> head()

Langkah yang akan dilakukan dalam mengeksplorasi dataset dimulai dari deskripsi umum mengenai dataset, statistik dataset, kemudian diakhiri dengan eksplorasi sebagai data deret waktu.

4.1 Deskripsi dataset

Dataset yang digunakan adalah exchange_r2. Deskrpsi yang dimaksud adalah eksplorasi dini terkait jumlah observasi/baris, jumlah variabel/kolom, dan periode datasetnya. Berikut kilasan exchange_r2.

exchange_r2 |> glimpse()
## Rows: 8,717
## Columns: 25
## $ period.unit        <date> 1999-01-04, 1999-01-05, 1999-01-06, 1999-01-07, 19…
## $ australian.dollar  <dbl> 1.910000, 1.894400, 1.882000, 1.847400, 1.840600, 1…
## $ canadian.dollar    <dbl> 1.800400, 1.796500, 1.771100, 1.760200, 1.764300, 1…
## $ swiss.franc        <dbl> 1.616800, 1.612300, 1.611600, 1.616500, 1.613800, 1…
## $ czech.koruna       <dbl> 35.10700, 34.91700, 34.85000, 34.88600, 34.93800, 3…
## $ danish.krone       <dbl> 7.450100, 7.449500, 7.445200, 7.443100, 7.443300, 7…
## $ uk.pound.sterling  <dbl> 0.7111000, 0.7122000, 0.7076000, 0.7058500, 0.70940…
## $ hong.kong.dollar   <dbl> 9.133200, 9.134100, 9.101000, 9.013100, 9.030200, 9…
## $ hungarian.forint   <dbl> 251.4800, 250.8000, 250.6700, 250.0900, 250.1500, 2…
## $ indonesian.rupiah  <dbl> 9433.610, 9314.510, 9337.680, 9218.770, 9321.630, 9…
## $ japanese.yen       <dbl> 133.7300, 130.9600, 131.4200, 129.4300, 130.0900, 1…
## $ korean.won         <dbl> 1398.590, 1373.010, 1359.540, 1337.160, 1366.730, 1…
## $ mexican.peso       <dbl> 11.64460, 11.59600, 11.47050, 11.55110, 11.44140, 1…
## $ malaysian.ringgit  <dbl> 4.479800, 4.480500, 4.463700, 4.420300, 4.429500, 4…
## $ norwegian.krone    <dbl> 8.855000, 8.774500, 8.733500, 8.629500, 8.590000, 8…
## $ new.zealand.dollar <dbl> 2.222900, 2.201100, 2.189000, 2.153100, 2.155700, 2…
## $ philippine.peso    <dbl> 45.51000, 44.74500, 44.87200, 44.43600, 44.29500, 4…
## $ polish.zloty       <dbl> 4.071200, 4.024500, 4.006500, 4.016500, 4.036300, 4…
## $ romanian.leu       <dbl> 1.311100, 1.316800, 1.316800, 1.309200, 1.314300, 1…
## $ swedish.krona      <dbl> 9.469600, 9.402500, 9.305000, 9.180000, 9.165000, 9…
## $ singapore.dollar   <dbl> 1.955400, 1.965500, 1.969900, 1.943600, 1.953700, 1…
## $ thai.baht          <dbl> 42.67990, 42.50480, 42.69490, 42.16780, 42.55900, 4…
## $ turkish.lira       <dbl> 0.3723000, 0.3728000, 0.3722000, 0.3701000, 0.37180…
## $ us.dollar          <dbl> 1.178900, 1.179000, 1.174300, 1.163200, 1.165900, 1…
## $ south.african.rand <dbl> 6.935800, 6.797500, 6.730700, 6.828300, 6.785500, 6…
exchange_r2$period.unit |> summary() |> strftime("%d %B %Y")
##               Min.            1st Qu.             Median               Mean 
##  "04 January 1999" "22 December 2004" "10 December 2010" "10 December 2010" 
##            3rd Qu.               Max. 
## "27 November 2016" "15 November 2022"
exchange_r2 |> dim()
## [1] 8717   25

Dari informasi diatas diketahui bahwa exchange_r2 memiliki \(8,717\) baris dan \(25\) kolom (\(24\) mata uang dengan \(1\) kolom waktu) yang dimulai dari \(\text{4 Januari 1999}\) sampai \(\text{15 November 2022}\). Pada tahap ini diambil keputusan untuk mengambil tiga mata uang saja untuk eksplorasi berikutnya sampai akhir. Mata uang yang digunakan antara lain indonesian.rupiah, us.dollar, japanese.yen. Kolom yang terpilih disimpan dalam objek exchange_r2_selected.

exchange_r2_selected <- exchange_r2 |> 
  select(period.unit, indonesian.rupiah, us.dollar, japanese.yen)
exchange_r2_selected |> glimpse()
## Rows: 8,717
## Columns: 4
## $ period.unit       <date> 1999-01-04, 1999-01-05, 1999-01-06, 1999-01-07, 199…
## $ indonesian.rupiah <dbl> 9433.610, 9314.510, 9337.680, 9218.770, 9321.630, 92…
## $ us.dollar         <dbl> 1.178900, 1.179000, 1.174300, 1.163200, 1.165900, 1.…
## $ japanese.yen      <dbl> 133.7300, 130.9600, 131.4200, 129.4300, 130.0900, 12…

Mata uang yang terpilih disimpan dalam variabel r2_selected.

r2_selected <- exchange_r2_selected |> 
  select(-period.unit) |> 
  colnames()

4.2 Statistik Deskriptif

Untuk tahap ini dan seterusnya akan menggunakan dataset exchange_r2_selected. Berikut summary() dari dataset.

exchange_r2_selected |> 
  select(-period.unit) |> 
  summary()
##  indonesian.rupiah   us.dollar       japanese.yen  
##  Min.   : 6708     Min.   :0.8252   Min.   : 89.3  
##  1st Qu.:11279     1st Qu.:1.0951   1st Qu.:117.1  
##  Median :12864     Median :1.1891   Median :128.7  
##  Mean   :12986     Mean   :1.1936   Mean   :127.6  
##  3rd Qu.:15412     3rd Qu.:1.3123   3rd Qu.:135.8  
##  Max.   :18240     Max.   :1.5990   Max.   :169.8

Dari informasi diatas diketahui nilai minimum, kuartil pertama/ketiga, median, rata-rata, dan maksimum. Nilai tiap kolom terlihat memiliki rentang (range) yang berbeda-beda. Sehingga jika dicoba untuk di plot distribusinya menggunakan boxplot akan berbeda.

exchange_r2_selected |>
  select(-period.unit) |> 
  boxplot()
Boxplot Nilai Tukar

Boxplot Nilai Tukar

Salah satu cara untuk melihat perbandingan antar kolom dengan dilakukan normalisasi. Normalisasi yang digunakan adalah menggunakan minimum dan maksimumnya (minmax scale). Berikut boxplot yang serupa setelah normalisasi.

exchange_r2_minmax <- exchange_r2_selected |> 
  mutate_if(
    is.numeric,
    function(.) {
      minvec <- min(.)
      maxvec <- max(.)
      normalize <- (. - minvec) / (maxvec - minvec)
    }
  )
exchange_r2_minmax |> 
  select(-period.unit) |> 
  boxplot()
Boxplot Nilai Tukar (normalisasi)

Boxplot Nilai Tukar (normalisasi)

Dari boxplot diatas, semua mata uang bisa dilihat distribusinya secara sekaligus. Mata uang japanese.yen memiliki beberapa potensi outlier. Sedangkan untuk indonesia.rupiah dan us.dollar tidak memilikinya. Hal tersebut bisa diinterpretasikan, bahwa pada indonesian.rupiah maupun us.dollar tidak ada lonjakan tajam yang diluar dari rata-rata maupun kuartilnya. Sedangkan untuk japanese.yen diduga terdapat posisi dimana mata uangnya yang berbeda dari seluruh observasi data lainnya.

exchange_r2_minmax |> 
  pivot_longer(cols = -period.unit, names_to = "currency", values_to = "rate") |> 
  arrange(currency) |> 
  ggplot(aes(x = period.unit, y = rate, color = currency)) +
  geom_line()
Perubahan Nilai Tukar Harian (Normalisasi)

Perubahan Nilai Tukar Harian (Normalisasi)

4.3 Eksplorasi Time Series

Untuk eksplorasi data deret waktu, yang digunakan hanyalah mata uang indonesian.rupiah.

exchange_r2_rupiah <- exchange_r2_selected |> 
  select(period.unit, indonesian.rupiah)
exchange_r2_rupiah |> glimpse()
## Rows: 8,717
## Columns: 2
## $ period.unit       <date> 1999-01-04, 1999-01-05, 1999-01-06, 1999-01-07, 199…
## $ indonesian.rupiah <dbl> 9433.610, 9314.510, 9337.680, 9218.770, 9321.630, 92…

Untuk eksplorasi berdasarkan jenis datanya, dibuat objek ts_rupiah yang mengubah objek exchange_r2_rupiah menjadi time series menggunakan ts().

ts_rupiah <- ts(exchange_r2_rupiah$indonesian.rupiah, start = c(1999, 1), frequency = 365)

Selanjutnya dilakukan decomposition untuk melihat karakteristik dataset ts_rupiah.

4.3.1 Dekomposisi

Dekomposisi dilakukan menggunakan fungsi decompose() yang membagi dataset menjadi tiga bagian utama yaitu trend, seasonal, dan error / remainder. Berikut objek dekomposisi decompose_rupiah beserta struktur datanya.

decompose_rupiah <- ts_rupiah |> decompose()
decompose_rupiah |> str()
## List of 6
##  $ x       : Time-Series [1:8717] from 1999 to 2023: 9434 9315 9338 9219 9322 ...
##  $ seasonal: Time-Series [1:8717] from 1999 to 2023: 173 175 181 141 94 ...
##  $ trend   : Time-Series [1:8717] from 1999 to 2023: NA NA NA NA NA NA NA NA NA NA ...
##  $ random  : Time-Series [1:8717] from 1999 to 2023: NA NA NA NA NA NA NA NA NA NA ...
##  $ figure  : num [1:365] 173 175 181 141 94 ...
##  $ type    : chr "additive"
##  - attr(*, "class")= chr "decomposed.ts"

Didapatkan tiga komponen dekomposisi yaitu seasonal, trend, dan random. Dan secara default dataset termasuk additive time series. Berikut grafik dekomposisi menggunakan autoplot().

decompose_rupiah |> autoplot()
Dekomposisi Nilai Tukar Rupiah

Dekomposisi Nilai Tukar Rupiah

Dari grafik dekomposisi diatas, terlihat bahwa hasil decompose() mengeluarkan tiga komponen utamanya. Akan tetapi, hasil diatas belum tentu sesuai dengan kenyataannya. Dan harus dieksplorasi lebih lanjut.

5 R3 - Pemodelan

Untuk pemodelan digunakan Holt Winters dan ARIMA. dataset yang digunakan adalah ts_rupiah. Tapi sebelumnya dataset harus dibagi menjadi dua bagian yaitu train set dan testing set.

window_selected <- 365
train_ts <- ts_rupiah |> head(-window_selected)
test_ts <- ts_rupiah |> tail(window_selected)

Pemodelan Holt Winters dan ARIMA menggunakan holtwinter dan stlm.

hw_rupiah <- HoltWinters(train_ts)
arima_rupiah <- stlm(train_ts, s.window = 365, method = "arima")

6 R4 - Evaluasi

Evaluasi

hw_forecast <- forecast(hw_rupiah, window_selected)
arima_forecast <- forecast(arima_rupiah, window_selected)
accuracy(hw_forecast, test_ts)
##                       ME     RMSE      MAE         MPE      MAPE      MASE
## Training set    3.164765 121.6350  80.6686  0.02337438 0.6513953 0.0688837
## Test set     -516.177619 910.2031 650.2115 -3.42822365 4.2676716 0.5552219
##                   ACF1 Theil's U
## Training set 0.5521309        NA
## Test set     0.9906554  14.43078
accuracy(arima_forecast, test_ts)
##                        ME      RMSE       MAE          MPE      MAPE       MASE
## Training set    0.7814501  75.46314  48.48109  0.004273649 0.4012051 0.04139847
## Test set     -594.6595784 762.99467 624.46642 -3.911100785 4.0932986 0.53323790
##                       ACF1 Theil's U
## Training set -0.0001222072        NA
## Test set      0.9865063092   12.0606
train_ts |> 
  autoplot() +
  autolayer(hw_rupiah$fitted[,'xhat'], series = "training") +
  autolayer(hw_forecast, series = "forecast") +
  autolayer(test_ts, series = "actual")
Training dan Prediksi menggunakan Holt-Winters

Training dan Prediksi menggunakan Holt-Winters

train_ts |> 
  autoplot() +
  autolayer(arima_rupiah$fitted, series = "training") +
  autolayer(hw_forecast, series = "forecast") +
  autolayer(test_ts, series = "actual")
Training dan Prediksi menggunakan Holt-Winters

Training dan Prediksi menggunakan Holt-Winters

7 Penutup

7.1 Kesimpulan

7.2 Saran

Berikut saran yang bisa diberikan:

  • Pada tahapan mengubah tipe data kolom, bisa dievaluasi setiap kolomnya atau kolom tertentu (tergantung kebutuhan bisnisnya). Sehinggga fokus eksplorasi data sudah ditentukan sejak awal yaitu sebelum masuk ke pra-pemrosesan data.
  • Di investigasi akhir coercion, dinyatakan bahwa “seluruh isiannya merupakan angka”. Hal tersebut bisa keliru jika terdapat isian yang merupakan angka tapi tidak valid. Semisal, nilai “1.23121312.3” akan dideteksi TRUE karena memiliki komponen :digit:, meski jika dilakukan as.numeric() akan muncul peringatan dan menghasilkan nilai NA. Hal tersebut dikarenakan penggunaan str_detect() dengan pattern = "[:digit:]".
  • Konsistensi penamaan dengan class objek. Dalam lembar kerja, objek missing_val merupakan list, sedangkan missing_val_actual merupakan data.frame. Untuk konsistensi dan memudahkan troubleshooting, sebaiknya dilakukan penamaan dan konsistensi tipe objeknya.