Iteration

Pada bagian ‘programming’, kita belajar bahwa salah satu prinsip dalam programming adalah ‘jangan mengopy paste kode kita sendiri’. Untuk menerapkan prinsip tersebut, kita diajakan cara untuk menggunakan fungsi yang memampukan kita untuk mengulang serangkaian perhitungan dan flow kode secara otomatis.

Cara lain adalah dengan teknik iterasi. Ada 2 paradigma iterasi : * Imperative programming; membuat iterasi eksplisit dengan for loop dan while loop * Functional programming; pembuatan fungsi untuk menduplikasi kode, sehingga for loop dengan kode yang sama akan mendapatkan fungsinya sendiri.

Prerequisites

library(tidyverse)
## Warning: package 'tidyverse' was built under R version 4.1.2
## -- Attaching packages --------------------------------------- tidyverse 1.3.1 --
## v ggplot2 3.3.5     v purrr   0.3.4
## v tibble  3.1.4     v dplyr   1.0.7
## v tidyr   1.1.4     v stringr 1.4.0
## v readr   2.0.2     v forcats 0.5.1
## Warning: package 'tidyr' was built under R version 4.1.2
## Warning: package 'dplyr' was built under R version 4.1.2
## -- Conflicts ------------------------------------------ tidyverse_conflicts() --
## x dplyr::filter() masks stats::filter()
## x dplyr::lag()    masks stats::lag()

Kenapa kita membutuhkan tidyverse? karena kita akan menggunakan pakcakge ‘purr’ yang berguna untuk kita bekerja dengan vector dan list.

Tapi sebelum penggunaan lebih jauh, kita akan belajar dari imperative programming dahulu.

For Loop

For loop adalah bentuk iterasi programming, di mana kita akan menentukan seberapa banyak kita melakukan pengulangan.

# contoh 1
for( i in 1:5){
  print(i)
}
## [1] 1
## [1] 2
## [1] 3
## [1] 4
## [1] 5
# contoh 2
for(i in 1:5){
  i = i +5
  print(i)
}
## [1] 6
## [1] 7
## [1] 8
## [1] 9
## [1] 10
# Contoh 3
abjad <- LETTERS[1:5]
for(i in abjad){
  print(i)
}
## [1] "A"
## [1] "B"
## [1] "C"
## [1] "D"
## [1] "E"
# Contoh 4
for(i in letters[1:5]) {
  print(i)
}
## [1] "a"
## [1] "b"
## [1] "c"
## [1] "d"
## [1] "e"

While Loop

While adalah bentuk lain dari metode iterasi yang tidak menentukan barapa kali iterasi dilakukan, namun memberi batas kondisional kapan iterasi berakhir. Untuk melakukannya, while loop biasanya memerlukan kita untuk menentukan kondisi initial.

# contoh1
xwhile1 <- 0
while(xwhile1 < 10){
  xwhile1 <- xwhile1 + 1
  print(xwhile1)
}
## [1] 1
## [1] 2
## [1] 3
## [1] 4
## [1] 5
## [1] 6
## [1] 7
## [1] 8
## [1] 9
## [1] 10

Break & Next

‘Break’ adalah iterator yang menghentikan loop saat mencapai kondisi tertentu. Sedangkan ‘Next’ adalah iterator yang digunakan untuk melewati titik loop tertentu, dan melanjutkannya sampai selesai.

for(i in 1:15){
  print(paste("number", i))
  if(i == 10){
    break
  }
}
## [1] "number 1"
## [1] "number 2"
## [1] "number 3"
## [1] "number 4"
## [1] "number 5"
## [1] "number 6"
## [1] "number 7"
## [1] "number 8"
## [1] "number 9"
## [1] "number 10"
x <- 10
while(x > 4){
  print(paste("i own",x))
  x <- x - 1
  if(x == 6){
    break
  }
}
## [1] "i own 10"
## [1] "i own 9"
## [1] "i own 8"
## [1] "i own 7"

Inilah cara kerja break.

Sementara untuk ‘Next’.

for(i in 1:15){
  if(i == 10){
    next
  }
  print(i)
}
## [1] 1
## [1] 2
## [1] 3
## [1] 4
## [1] 5
## [1] 6
## [1] 7
## [1] 8
## [1] 9
## [1] 11
## [1] 12
## [1] 13
## [1] 14
## [1] 15

Sudah mulai paham maksudnya ya?

Sekarang kita coba kembangakan pembahasan dengan mengaitkan for loop dengan tibble dan lainnya.

df <- tibble(
  a = rnorm(10),
  b = rnorm(10),
  c = rnorm(10),
  d = rnorm(10)
)

output <- vector("double", ncol(df))
for(i in seq_along((df))){
  output[[i]] <- median(df[[1]])
}

output
## [1] 0.2863159 0.2863159 0.2863159 0.2863159

Kenapa lebih ribet ? bukan. Tapi cara di atas adalah teknik untuk melakukan loop per kolom. Exercise

  1. Hitung mean dari seluruh kolom dalam data set mtcars
outcars <- vector("double",length(mtcars))
for(i in seq_along(mtcars)){
  outcars[[i]] <- mean(mtcars[[i]])
}

outcars
##  [1]  20.090625   6.187500 230.721875 146.687500   3.596563   3.217250
##  [7]  17.848750   0.437500   0.406250   3.687500   2.812500
  1. Tentukan type dari setiap kolom dalam ’nycflights13::flights
# upload datanya dulu
library(nycflights13)
## Warning: package 'nycflights13' was built under R version 4.1.2
# Buat vector
outflights <- vector("character",length(flights))

# Buat loop
for(i in seq_along(flights)){
  outflights[[i]] <- typeof(flights[[i]])
}

outflights
##  [1] "integer"   "integer"   "integer"   "integer"   "integer"   "double"   
##  [7] "integer"   "integer"   "double"    "character" "integer"   "character"
## [13] "character" "character" "double"    "double"    "double"    "double"   
## [19] "double"
  1. Hitung angka unik dalam tiap kolom di iris
# buat vector
outiris <- vector("numeric", length(iris))

# BUat loop
for(i in seq_along(iris)){
  outiris[[i]] <- length(unique(iris[[i]]))
}

outiris
## [1] 35 23 43 22  3
  1. Hasilkan 10 angka random berdistribusi normal dengan rata2 : -10, 0, 10, dan 100
# Untuk kasus ini, kita membutuhkan list, bukan vector
vec_mean <- c(-10,0,10,100)
list_norm2 <- list_along(vec_mean)

# Buat loop
for(i in seq_along(vec_mean)){
  list_norm2[[i]] <- rnorm(10,vec_mean[[i]])
}
# print output
list_norm2
## [[1]]
##  [1] -11.147581  -9.480504  -9.814152 -12.041669 -10.115821  -9.758218
##  [7]  -9.831882  -9.330711 -12.444350  -9.900217
## 
## [[2]]
##  [1]  1.0398104 -2.6185215  0.3444442  0.2720242 -0.2106089  0.3862725
##  [7] -0.4188405 -0.6940779  0.8888500  0.7175655
## 
## [[3]]
##  [1]  9.750837  9.277120 10.382698 10.076400  9.645826  9.803784 10.122800
##  [8] 10.227116 11.475699  9.352878
## 
## [[4]]
##  [1]  99.67852 100.91911  99.27784  99.46244 100.98002  99.01640  99.31053
##  [8]  99.06962 100.12311  99.52313
  1. Hilangkan fungsi for loop dan ganti dengan fungsi vector yang lebih sederhana.
out <- ""
for(x in letters){
  out <- stringr::str_c(out,x)
}

Jawab :

# Fungsi di atas bertujuan untuk menggabungkan seluruh element 'letters' menjadi satu vector. Sehingga kita bisa sederhankan fungsi di atas tanpa for loop dengan kode di bawah ini

out <- str_c(letters, collapse = ",")

out
## [1] "a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z"

5.1 Hilangkan juga for loop pada fungsi di bawah ini.

x <- sample(100)
sd <- 0
for(i in seq_along(x)){
  sd <- sd + (x[i]) - mean(x))^2
}
sd <- sqrt(sd / (length(x)-1))
# fungsi di atas pada dasarnya cuma menerapkan fungsi yang sudah ada dalam fungsi sd() dengan fungsi for loop. 

# Buat kode tanpa for loop
x <- sample(100)
sd(x, na.rm = TRUE)
## [1] 29.01149
# Komparasi
sd2 <- 0
for(i in seq_along(x)){
  sd2 <- sd2 + (x[i] - mean(x))^2
}
sd2 <- sqrt(sd2/(length(x)-1))

sd2
## [1] 29.01149

Sama, kan?

# Hilangkan loop pada kode berikut ini :
try3 <- runif(100)
out3 <- vector("numeric", length(try3))
out3[1] <- try3[1]

for(i in 2:length(try3)){
  out3[i] <- out3[i-1] + try3[i]
}
out3
##   [1]  0.08466832  0.22794870  0.61410034  1.14457518  1.36308047  1.78577738
##   [7]  2.16436279  2.75507834  2.83007243  3.11955880  3.87159996  4.79863231
##  [13]  5.01295449  5.89137636  6.01390034  6.06444756  6.48229372  6.79075416
##  [19]  6.88283673  7.78186383  8.51864509  9.27320848  9.41862984  9.73409609
##  [25] 10.72028167 11.66483787 12.39332001 13.32644805 13.39687451 14.30626784
##  [31] 15.08520554 15.15627944 15.24112996 16.07629471 16.41221583 16.91705695
##  [37] 17.76652487 18.70502559 18.95843640 19.09845826 19.64566277 20.18096166
##  [43] 21.11259568 22.00091831 22.30072872 23.19119054 23.27162079 23.66636776
##  [49] 23.68250700 24.44193054 25.37639372 26.23927892 26.32717311 26.71051031
##  [55] 26.78165715 27.28605873 27.87454648 28.74393685 29.45205805 30.30120698
##  [61] 30.30559239 30.48860403 30.82906218 31.14677723 31.54727685 31.62638030
##  [67] 32.41869097 32.90350396 33.62644302 34.07989492 34.08421851 34.13330036
##  [73] 35.05398721 35.90889134 36.24862963 36.78699815 37.64973872 37.85917729
##  [79] 38.85217119 39.16720154 39.85332332 40.44987340 40.68829220 41.38901196
##  [85] 41.58014645 42.16582753 42.65091941 42.77619903 43.09494937 43.53317521
##  [91] 44.01704113 44.31755230 44.91007587 45.27580728 45.79713698 46.40367996
##  [97] 46.88640707 46.92899861 47.92878285 48.00866303
# Tujuan dari kode ini adalah membuat vector baru yang berisi 100 nilai dari penjumlahan deret value variabel 'try3'. Untuk menghilangkan loop ini, kita bisa menggunakan vector; Kita tahu bahwa vector output akan berisi nilai deret, jadi tinggal kita buat dua vector, satu vector berisi value sumber, dan satu vector berisi nilai yang sama, hanya saja selisih satu deret di awal.

out3 <- cumsum(try3)

out3
##   [1]  0.08466832  0.22794870  0.61410034  1.14457518  1.36308047  1.78577738
##   [7]  2.16436279  2.75507834  2.83007243  3.11955880  3.87159996  4.79863231
##  [13]  5.01295449  5.89137636  6.01390034  6.06444756  6.48229372  6.79075416
##  [19]  6.88283673  7.78186383  8.51864509  9.27320848  9.41862984  9.73409609
##  [25] 10.72028167 11.66483787 12.39332001 13.32644805 13.39687451 14.30626784
##  [31] 15.08520554 15.15627944 15.24112996 16.07629471 16.41221583 16.91705695
##  [37] 17.76652487 18.70502559 18.95843640 19.09845826 19.64566277 20.18096166
##  [43] 21.11259568 22.00091831 22.30072872 23.19119054 23.27162079 23.66636776
##  [49] 23.68250700 24.44193054 25.37639372 26.23927892 26.32717311 26.71051031
##  [55] 26.78165715 27.28605873 27.87454648 28.74393685 29.45205805 30.30120698
##  [61] 30.30559239 30.48860403 30.82906218 31.14677723 31.54727685 31.62638030
##  [67] 32.41869097 32.90350396 33.62644302 34.07989492 34.08421851 34.13330036
##  [73] 35.05398721 35.90889134 36.24862963 36.78699815 37.64973872 37.85917729
##  [79] 38.85217119 39.16720154 39.85332332 40.44987340 40.68829220 41.38901196
##  [85] 41.58014645 42.16582753 42.65091941 42.77619903 43.09494937 43.53317521
##  [91] 44.01704113 44.31755230 44.91007587 45.27580728 45.79713698 46.40367996
##  [97] 46.88640707 46.92899861 47.92878285 48.00866303

For loop variation

Ada 4 variasi penggunaan for loop : * menugubah objek existing, bukan membuat objek baru. * Looping menggunakan nama value * Mengelola output loop yang tidak diketahui panjangnya * Mengelola sequence loop yg tidak diketahui panjangnya.

Mengubah objek existing

Ada kalanya kita ingin mengubah value dari objek yang sudah ada. Untuk mempermudah tugas ini, kita bisa menggunakan for loop.

df2 <- df

# Katakan, kita ingin mengubah skala nilainya. Di sini, kita akan menggunakan fungsi

rescale01 <- function(x){
  rng <- range(x, na.rm = TRUE)
  (x - rng[1])/(rng[2]-rng[1])
}

# Skg, kita buat loop untuk menerapkan fungsi di atas.

for(i in seq_along(df2)){
  df2[[i]] <- rescale01(df2[[i]])
}
df2
## # A tibble: 10 x 4
##        a     b     c       d
##    <dbl> <dbl> <dbl>   <dbl>
##  1 0.612 0.778 0.250 0      
##  2 0.539 0     0     0.237  
##  3 0     0.385 0.195 0.414  
##  4 0.467 0.402 1     1      
##  5 0.305 0.493 0.321 0.758  
##  6 0.395 0.628 0.760 0.553  
##  7 1     0.308 0.256 0.662  
##  8 0.756 0.900 0.177 0.00581
##  9 0.711 1     0.839 0.609  
## 10 0.329 0.697 0.683 0.227

Menangani operasi loop pada vector yang panjangnya tidak diketahui

Kita bisa membuat vector yang bertambah, seperti :

means <- c(0,1,2) # Kita ingin memproduksi random vector dengan mean dari vector ini
output1 <- double() # Kita buat objek vector double dengan panjang 0

for(i in seq_along(means)){ # Kita lakukan loop sepanjang sumber means
  n <- sample(100,1) # kita buat objek 'n' untuk membuat panjang vector yang random
  output <- c(output1, rnorm(n, means[[i]])) # length dari 'n' dan mean dari 'means' taruh di sini
}
output
##  [1]  0.66251215  3.34491076  1.34571442  2.67541889  2.25385148  1.15046101
##  [7]  2.66867195  2.15576733  1.76114142  2.16854421  1.05646577  1.72008598
## [13]  2.37264887  3.28280194  2.39113151  0.11978537  2.21996246  4.49411884
## [19]  1.91070132  3.28879092  0.60605940  3.58081308  0.82717145  2.63104430
## [25]  0.56766215  2.02176122  0.62805143  1.06260153  2.17670442  3.91860730
## [31]  3.44645460  1.24370212  2.33208947  1.68116587  2.86881011  2.05893238
## [37]  1.24788361  2.29304035  2.51792621  2.92337566  3.01797617  2.96903575
## [43]  2.58177092 -0.06216844  1.83611421  1.07424810  2.70649245  0.75370000
## [49]  3.36601415  2.55186721  1.96745551  2.60961761  0.89176291  0.05247917
## [55]  2.23475348  4.12542681 -0.71060515  0.79306318  1.76341674  1.97119581
## [61]  3.16616210  2.24034054  2.51699444  1.50751544  1.23453208  1.42134948
## [67]  4.91133717  4.44539364  2.99238268  3.73686272  3.08340524  0.59753834
## [73]  1.51200588  1.77723003  1.98683030  3.06349182  2.54450341  2.22739806
## [79]  2.42489490  2.14354519  2.32251782  2.54828753  0.55278917

Tapi cara yang lebih clear adalah dengan membuat objek list, kemudian, melalukan loop pada fungsi tersebut.

out5 <- vector("list", length(means))
for(i in seq_along(means)){
  n <- sample(100,1)
  out5[[i]] <-  rnorm(n, means[[i]])
}
out5
## [[1]]
## [1]  0.13084963 -1.76434798 -0.52419357 -1.54806048  0.36852692 -0.01271746
## 
## [[2]]
##  [1]  0.22250700  1.13773854  0.74301159  1.89127975 -0.48460338  0.62691237
##  [7]  1.65581137 -0.42568114  2.70226581  0.22448984  0.06571968 -0.31588005
## [13]  1.66815666  2.60578996  1.55721819 -1.41851491  1.23774207  1.69023324
## [19]  0.66002736  0.40599468  0.70404360  1.71785578  1.61015066  1.76384303
## [25]  0.30331643  0.23898690  0.56114845 -0.51919877  0.32858308 -1.38824322
## [31]  0.31554103 -0.57389557  1.30358043  2.20710596
## 
## [[3]]
##  [1]  2.147986353  1.620604600  2.887687008  4.162298561  2.705229183
##  [6] -0.570776061  2.065916310  2.634594830  1.507002969  1.311254667
## [11]  2.593714770  1.283016843  2.280371008  2.571921590  1.842858853
## [16]  2.381804430  1.300776683  1.526213344  3.348165649  0.501554446
## [21]  2.916137931  1.698571792  2.602803685  0.105977341  1.485074308
## [26]  1.254850659  0.008425524  1.350578735  2.354821393  3.238498837
## [31]  1.180009871  2.618278508  1.646546742  2.341305348  0.479017102
## [36]  0.099279662  2.054158475  0.839902395  1.923350111  2.844634959
## [41]  0.424769636  0.926793981  3.532231055  1.926928844  1.570620433
## [46]  2.593162630  2.760572728  2.024473427  1.862370145  1.093432389
## [51]  1.743306656  2.419392883  1.341527070  0.062945891  2.003947697
## [56]  1.834358288  1.492672908  1.041939792  1.180280499  1.325774517
## [61]  1.416971045  0.590716936  2.617245642  3.029479557  0.817152827
## [66]  1.467448435  2.537368207  2.485134286  3.853512580  0.898008997
## [71]  2.271889612  2.735310893  2.437009557  1.091512276  2.613498988
## [76]  2.148631743  1.819176835  2.269460654  0.983616959
# Untuk menjadikannya satu vector 
unlist(out5)
##   [1]  0.130849627 -1.764347978 -0.524193567 -1.548060476  0.368526918
##   [6] -0.012717456  0.222507003  1.137738538  0.743011589  1.891279747
##  [11] -0.484603375  0.626912373  1.655811368 -0.425681141  2.702265807
##  [16]  0.224489843  0.065719676 -0.315880048  1.668156662  2.605789957
##  [21]  1.557218187 -1.418514913  1.237742066  1.690233236  0.660027363
##  [26]  0.405994677  0.704043602  1.717855779  1.610150658  1.763843028
##  [31]  0.303316426  0.238986896  0.561148446 -0.519198769  0.328583083
##  [36] -1.388243218  0.315541027 -0.573895567  1.303580426  2.207105957
##  [41]  2.147986353  1.620604600  2.887687008  4.162298561  2.705229183
##  [46] -0.570776061  2.065916310  2.634594830  1.507002969  1.311254667
##  [51]  2.593714770  1.283016843  2.280371008  2.571921590  1.842858853
##  [56]  2.381804430  1.300776683  1.526213344  3.348165649  0.501554446
##  [61]  2.916137931  1.698571792  2.602803685  0.105977341  1.485074308
##  [66]  1.254850659  0.008425524  1.350578735  2.354821393  3.238498837
##  [71]  1.180009871  2.618278508  1.646546742  2.341305348  0.479017102
##  [76]  0.099279662  2.054158475  0.839902395  1.923350111  2.844634959
##  [81]  0.424769636  0.926793981  3.532231055  1.926928844  1.570620433
##  [86]  2.593162630  2.760572728  2.024473427  1.862370145  1.093432389
##  [91]  1.743306656  2.419392883  1.341527070  0.062945891  2.003947697
##  [96]  1.834358288  1.492672908  1.041939792  1.180280499  1.325774517
## [101]  1.416971045  0.590716936  2.617245642  3.029479557  0.817152827
## [106]  1.467448435  2.537368207  2.485134286  3.853512580  0.898008997
## [111]  2.271889612  2.735310893  2.437009557  1.091512276  2.613498988
## [116]  2.148631743  1.819176835  2.269460654  0.983616959

Hitung mean dari tibble berikut ini!

df3 <-  tibble(
  a1 <- runif(50),
  a2 <- rnorm(50,14),
  a3 <- runif(50),
  a4 <- rnorm(50,8),
  a5 <- runif(50)
)
df3[is.na(df3)] <- 0

outdf <- vector("double", length(df3))
for(i in seq_along(df3)){
  outdf[[i]] <- mean(na.omit(df3[[i]]))
}

outdf
## [1]  0.4782025 13.9956207  0.4784222  8.0571682  0.4866499

Kita sudah sering melakukan ini (menghitung mean dari tiap kolom), oleh karena itu, akan lebih baik kita memasukkan ini ke dalam fungsi, dan kita beri nama col_mean()

col_mean <- function(df) {
  outdf <- vector("double", length(df))
  for(i in seq_along(df)){
    outdf[[i]] <- mean(df[[i]])
  }
  outdf
}

Sekarang, kita coba fungsi kita.

col_mean(df3)
## [1]  0.4782025 13.9956207  0.4784222  8.0571682  0.4866499

Oke. Sekarang pertanyaannya, bagaimana kita menghitung median, dan sd? apa itu berarti kita harus membuat fungsi untuk masing-masing rumus?

Untungnya tidak. Pikirkan seperti ini :

# Kita punya fungsi seperti ini
f1 <-  function(x) abs(x-mean(x))^1
f2 <- function(x) abs(x-mean(x))^2
f3 <- function(x) abs(x-mean(x))^3

Perhatikan bahwa hanya pada bagian ‘^..’ yg berbeda. Di sini, kita bisa menggunakan for loop dalam function kita.

f <- function(x,i) {
  outx <- abs(x - mean(x))^i # fungsi dengan dua objek argumen, vector x dan i iterasi
  return(outx)
}
try5 <- c(1:5)
f(try5, 2)
## [1] 4 1 0 1 4

Dengan fungsi di atas, kita bisa bebas menentukan berapa pangkat yg dibutuhkan.

Kembali ke persoalan di atas. Kita butuh satu fungsi yang bisa melayani tiga perhitungan sekaligus

df_summar <- function(df, fun){
  outdf <- vector("double", length(df))
  for(i in seq_along(df)){
    outdf[[i]] <- fun(df[[i]])
  }
  outdf
}

Sekarang kita uji function di atas

df_summar(df3, mean)
## [1]  0.4782025 13.9956207  0.4784222  8.0571682  0.4866499
df_summar(df3, median)
## [1]  0.4722212 13.8458023  0.3688759  8.1163350  0.4536852
df_summar(df3, sd)
## [1] 0.2888212 0.9726234 0.2752003 1.0272535 0.2938793

Map Function

Proses perhitungan fungsi dan loop ke setiap elemen pada list, vector, atau data frame sering dilakukan, sehingga memotivasi munculnya package purr. Package ini akan memudahkan kita untuk menerapkan fungsi pada setiap elemen vector sehingga bisa dibilang menggantikan fungsi for loop.

Ada beberapa syntax dalam purr : * map() – > menghasilkan list * map_lgl() –> membuat vector logika * map_int() –> membuat vector integer * map_dbl() –> membuat vector double * map_chr() –> membuat vector character

Setiap fungsi di atas akan mengambil input sebuah vector, menerapkan fungsi pada masing-masing elemen, dan menghasilkan vector baru dengan panjang yang sama.

contoh :

map_dbl(df3, mean)
##     a1 <- runif(50) a2 <- rnorm(50, 14)     a3 <- runif(50)  a4 <- rnorm(50, 8) 
##           0.4782025          13.9956207           0.4784222           8.0571682 
##     a5 <- runif(50) 
##           0.4866499
map_dbl(df3, median)
##     a1 <- runif(50) a2 <- rnorm(50, 14)     a3 <- runif(50)  a4 <- rnorm(50, 8) 
##           0.4722212          13.8458023           0.3688759           8.1163350 
##     a5 <- runif(50) 
##           0.4536852
map_dbl(df3, sd)
##     a1 <- runif(50) a2 <- rnorm(50, 14)     a3 <- runif(50)  a4 <- rnorm(50, 8) 
##           0.2888212           0.9726234           0.2752003           1.0272535 
##     a5 <- runif(50) 
##           0.2938793

argument kedua dalam map_*(), ‘.f’, dapat diisi dengan formula, vector character, atau vector integer

Shortcut

Eksistensi fungsi map sangat membantu dalam penulisan kode yang lebih jelas. Namun, map function memfasilitasi beberapa kemudahan lainnya untuk mempersingkat kode kita.

Anggaplah kita ingin membuat model linear untuk masing-masing grup dari sebuah dataset (contoh : mtcars). Di sini, kita akan menghitung model linear dari variabel ‘mpg’ dengan ‘wt’ berdasarkan klasifikasi banyaknya silinder mesinnya (‘cyl’).

models <- mtcars %>% 
  split(.$cyl) %>%
  map(function(df) lm(mpg~wt, data = df))

# Output
models
## $`4`
## 
## Call:
## lm(formula = mpg ~ wt, data = df)
## 
## Coefficients:
## (Intercept)           wt  
##      39.571       -5.647  
## 
## 
## $`6`
## 
## Call:
## lm(formula = mpg ~ wt, data = df)
## 
## Coefficients:
## (Intercept)           wt  
##       28.41        -2.78  
## 
## 
## $`8`
## 
## Call:
## lm(formula = mpg ~ wt, data = df)
## 
## Coefficients:
## (Intercept)           wt  
##      23.868       -2.192

Penulisan kode di atas sudah benar, tapi kita bisa merampingkannya lebih baik lagi.

models2 <- mtcars %>% 
  split(.$cyl) %>%
  map(~lm(mpg~wt, data = .))


# Output
models2
## $`4`
## 
## Call:
## lm(formula = mpg ~ wt, data = .)
## 
## Coefficients:
## (Intercept)           wt  
##      39.571       -5.647  
## 
## 
## $`6`
## 
## Call:
## lm(formula = mpg ~ wt, data = .)
## 
## Coefficients:
## (Intercept)           wt  
##       28.41        -2.78  
## 
## 
## $`8`
## 
## Call:
## lm(formula = mpg ~ wt, data = .)
## 
## Coefficients:
## (Intercept)           wt  
##      23.868       -2.192

Perbedaannya di sini hanya lah pada penggantinan function(x) menjadi ~, dan data = df menjadi data = .

Seringkali setelah melakukan analisa model linier, kita ingin melihat nilai dari karakterisitk model linier seperti R2.

models2 %>%
  map(summary) %>% map_dbl(~.$r.squared)
##         4         6         8 
## 0.5086326 0.4645102 0.4229655

Atau, lebih mudah,

models2 %>% 
  map(summary)%>% map_dbl("r.squared")
##         4         6         8 
## 0.5086326 0.4645102 0.4229655

Mapping over multiple input

Sejauh ini kita hanya melakukan mapping dengan menggunakan satu input vector/list. Lalu bagaimana jika kita ingin melakukan mapping terhadap dua atau lebih?

Untuk kasus ini, kita akan menggunakan map2() atau pmap()

Contoh :

# Kita ingin memproduksi angka random dari mean tertentu.

mu <- list(5,3,-2) # daftar mean
mu %>%  map(rnorm, n = 10)
## [[1]]
##  [1] 4.456102 4.300449 4.987564 5.615094 4.282848 5.971350 5.201387 5.649292
##  [9] 5.814305 6.140520
## 
## [[2]]
##  [1] 3.472888 3.625781 4.354506 2.187792 2.009978 2.356148 3.537119 4.018844
##  [9] 2.546483 3.018298
## 
## [[3]]
##  [1] -1.2974064 -0.6125273 -1.9629513 -3.0970530 -3.0461386  0.1447438
##  [7] -2.1153976 -1.8221693 -1.2143205 -1.3436924

Untuk ini, seharusnya kita sudah paham. Tapi bagaimana jika kita ingin menambahkan ketentuan standart deviasinya?

# kita buat list untuk ketentuan standart deviasinya
sigma <- list(1,5,10)
seq_along(mu)%>%
map(~rnorm(10, mu[[.]], sigma[[.]]))
## [[1]]
##  [1] 6.209908 4.707214 3.242299 4.963985 3.949342 4.704642 3.128343 3.401150
##  [9] 3.728241 3.057595
## 
## [[2]]
##  [1]  1.750153 -8.590564  5.815063 -4.665086  4.104495  2.435162  3.109084
##  [8]  7.515552  9.332409  6.645788
## 
## [[3]]
##  [1]   1.9015259   7.8963291  -8.5919666   2.2926267   0.3849889 -25.8692201
##  [7]  -6.9866434   7.4961327   6.7488478   6.9570681

Atau kita bisa pakai fungsi map2.

map2(mu, sigma, rnorm, n = 5) 
## [[1]]
## [1] 2.951634 4.848983 3.896074 5.830847 4.187881
## 
## [[2]]
## [1]  5.436249 -7.332634  9.185904  3.891666  8.211501
## 
## [[3]]
## [1]  3.691802 -1.564784  9.522733 -3.931461 -6.396568

Contoh penggunaan fungsi pmap() :

n <- list(1,3,5)
arg1 <- list(n, mu, sigma)
arg1 %>% pmap(rnorm)
## [[1]]
## [1] 4.389728
## 
## [[2]]
## [1] 9.1949996 0.7935223 1.4804367
## 
## [[3]]
## [1] 11.124285  6.772189 -3.468397 -2.814180 -4.973266

Jika dari tadi kita membahas soal multi list input, bagaimana jika yang beragam adalah fungsinya?

Untuk kasus multi fungsi, kita gunakan fungsi invoke_map() :

# buat list fungsi yang ingin ditampilkan
f_list <- list(runif, rnorm, rpois)
param <- list(
  list(min = -1, max = 1),
  list(sd = 5),
  list(lambda = 10)
)

# fungsi

invoke_map(f_list, param, n = 5)
## [[1]]
## [1]  0.55266664 -0.85304837 -0.06279371 -0.86071173 -0.82650420
## 
## [[2]]
## [1] -6.486355 -1.305149  5.739602  3.241884 -9.091464
## 
## [[3]]
## [1] 11 11 14  7 11

Argumen pertama dari invoke_map adalah nama fungsi atau vector character dari fungsi. Argumen kedua adalah list dari list yang memberi argumen pada masing-masing fungsi.

fungsi lain loop

Ada beberapa fungsi lain yg juga menerapkan teknik iterasi for loop : * Predicate function –> fungsi loop yg diterapkan dengan logika TRUE dan FALSE * keep() akan menyaring dan menyimpan data yang TRUE * discard() akan menyaring dan menyimpang data FALSE * some() akan memberi hasil logic jika ada beberapa elemen dalam data yg TRUE * every() akan memberi hasil logic jika SELURUH elemen dalam data TRUE * detect() akan memberi output berupa elemen pertama yang predicate TRUE * detect_index() akan memberi output index elemen pertama yg predicate TRUE * head_while() akan memberi output beberapa elemen pertama yg predicate TRUE * tail_while() akan memberi output beberapa elemen terakhir yg predicate TRUE

  • reduce and accumulate – > fungsi loop yg diterapkan untuk mengurangi dan mengakumulasi list
    • reduce()
    • accumulate()