Statistik JS: Chap. 6

Riky Perdana
30 min readDec 27, 2022

--

Photo by Alexey Savchenko on Unsplash

6. Journey Through Time

Data time series adalah data dimana salah satu dimensinya adalah waktu. Dimensi waktu ini biasanya memiliki interval yang homogen, contohnya data tahunan, bulanan, mingguan, harian, jam, menit, hingga detik. Disebut homogen karena tidak boleh mencampurkan data tahunan dan harian dalam 1 kelompok data yang sama. Data time series (berurut waktu) juga biasanya tersusun secara serial (interval yang terjaga tanpa ada unit waktu yang terlewat, walau tidak mutlak wajib). Konsistensi dan serialisasi mencirikan kelompok data time series yang bagus untuk dianalisa. Pada umumnya data time series dikumpulkan dan dianalisa untuk tujuan peramalan nilai data berikutnya yang belum terjadi berdasarkan telaahan data historis yang telah berlalu. Sebenarnya data time series dapat digunakan untuk tujuan hal diluar peramalan seperti korelasi dan kausalitas, tapi buku ini akan fokus dulu pada tujuan peramalan.

Sekarang Anda telah tahu bahwa salah satu dimensinya adalah waktu, lalu dimensi apa yang bisa menjadi pasangannya? Bisa apapun! Selagi nilainya bersifat interval atau rasio. Bisa juga untuk data ordinal walau tidak lumrah. Yang jelas tidak bisa untuk data nominal yang sifatnya klasifikasi. Apa contoh data interval yang bisa diramalkan? Banyak! Mulai dari ramalan cuaca, penjualan produk, sampai pergerakan benda kosmik. Namun perlu diingat bahwa yang namanya peramalan akan selalu bersifat probabilistik, dimana hasil perkiraan hanya bisa mendekati realita pada titik waktu yang diramalkan. Dan bilamana ada hasil ramalan yang eksak sama dengan realita yang terjadi maka itu bisa jadi murni kebetulan belaka. Hal terbaik yang bisa dilakukan ahli statistik adalah memilih alat yang tepat, sumber data yang baik, dan membuat persamaan ramalan yang paling mampu mendekati realita.

Teknik peramalan yang akan digunakan oleh seorang ahli statistik tergantung pada pola trend mana yang tampak pada grafik. Secara umum pola tersebut ada yang linear (garis lurus), cyclical (bersiklus), seasonal (musiman), dan residual. Menggunakan teknik peramalan linear pada pola cyclical tentu akan menghasilkan ramalan yang jauh dari ekspektasi dan realita, begitu pula dengan pola lainnya. Maka dari itu, tahap pertama dalam peramalan justru adalah menggambar grafik, agar ahli statistiknya dapat melakukan inspeksi visual dan mengklasifikasikan pola apa yang tampak pada grafik tersebut. Tahapan kedua adalah memilih alat statistik mana yang akan digunakan, ini panduannya:

A. Pola Linier:

  1. Freehand (tangan bebas)
  2. Half average (setengah rata-rata)
  3. Moving average (rata-rata bergerak)
  4. Least square (kuadrat terkecil)
  5. Mathematical model (model matematis)

B. Pola Cyclical:

  1. Quadratic equation (persamaan kuadrat)

C. Pola Seasonal:

  1. Total average (rata-rata total)
  2. Simple average (rata-rata sederhana)
  3. Ratio method (metode rasio)
  4. Moving ratio (rasio bergerak)

Lumayan beragam juga kan teknik peramalan untuk setiap pola? Selain dari yang ditunjukkan diatas, di luar sana masih lebih banyak lagi metode peramalan berbasis statistik yang jauh lebih advanced, bahkan artificial intelligence (kecerdasan buatan) diciptakan juga untuk melakukan peramalan data. Sebagai contoh gambar seekor kucing dalam resolusi 100*100 pixel dikonversi menjadi data himpunan 10000 integer, diajarkan bahwa himpunan data itu adalah citra kucing. Lakukan ulang untuk beberapa ratus ribu foto kucing lainnya dengan dimensi piksel yang sama, bila rancangan AI-nya memang bagus, pada foto berikutnya ia bisa menebak apakah dalam foto yang baru ada kucingnya atau tidak. Itu adalah salah satu contoh paling kompleks dari pemanfaatan model matematis untuk peramalan data. Dalam buku ini kita cukup memulai dengan statistik yang sederhana untuk peramalan data hingga memperoleh informasi yang berharga.

6.1. Metode Freehand

Photo by Green Chameleon on Unsplash

Metode ini adalah metode peramalan berbasis grafik yang paling mudah (malas?). Bahkan diragukan bahwa ia masuk kedalam kategori statistik atau tidak. Karena dalam metode freehand sama sekali tidak menggunakan perhitungan matematis melainkan kemampuan individu seseorang untuk melakukan inspeksi visual, interpretasi trend, dan menggambar lanjutan grafik tersebut cukup dengan menggunakan tangan. Berikut ini adalah contoh grafik sederhana berikut dengan contoh freehandnya.

Cara melakukan peramalan freehand:

  1. Gambarkan sebuah grafik berdasarkan suatu set data pada kertas
  2. Ambil penggaris transparan, letakkan diatas grafik sehingga tepinya membentuk garis lurus yang berposisi paling mendekati bentuk lurus grafik tersebut
  3. Gunakan pensil, buat garis baru berdasarkan tepian penggaris tersebut
  4. Hasil peramalan garis lurus berdasarkan metode freehand siap dihidangkan

Berikut ini adalah daftar kelebihan freehand:

  1. Tidak memerlukan perhitungan matematis
  2. Jika dilakukan oleh orang yang berpengalaman melakukan freehand maka hasilnya mungkin dapat mendekati hasil perhitungan matematis

Tapi kelemahannya adalah sebagai berikut:

  1. Ketepatan bergantung pada pengalaman subjektif individu penggambar
  2. Hasil dapat inkonsisten antara satu penggambar dengan penggambar yang lain
  3. Sulit untuk mengajukan argumen kuantitatif berdasarkan keputusan subjektif

6.2. Semi average

Gambar: Contoh grafik Semi-Average

Bila dalam metode freehand kita menyerahkan justifikasi garis lurus kepada si penggambarnya, maka dalam semi average kita menyerahkan penggaris tersebut kepada matematika. Artinya garis lurus yang paling mewakili sebuah grafik tidak lagi dikerjakan secara subjektif melainkan sudah secara objektif kuantitatif oleh model matematika.

Cara kerja metode semi average:

  1. Buat tabel dengan 3 kolom (tahun, nilai, semitotal, semiaverage)
  2. Setiap barisnya mewakili nilai pada tahun tersebut
  3. Bagi dua data tersebut berdasarkan rentang tahun
  4. Untuk setiap bagian, jumlahkan nilainya dan bagi dengan panjang rentang bagian tersebut
  5. Sekarang kita sudah mendapatkan 2 angka (part 1, dan part 2) sebagai pivot
  6. Cari selisih nilai antara part 1 dan part 2 untuk mendapatkan angka kenaikan trend
  7. Buat sebuah kolom baru bernama nilai prediksi
  8. Isi kolom tersebut dengan pengurangan/penambahan berdasarkan jarak minus/plus dari part 1 dikalikan dengan angka kenaikan trend
  9. Kolom terakhir inilah yang menjadi hasil peramalan dengan metode semi-average

Kelebihan metode semi average:

  1. Model matematikanya relatif sederhanya, hanya kali-bagi-tambah-kurang
  2. Hasil perhitungannya lebih dapat diandalkan karena menggunakan model matematis
  3. Hasil perhitungannya lebih konsisten, terlepas dari siapa yang mencoba menghitungnya

Kekurangan metode semi average:

  1. Perhitungan yang sederhana tidak selalu menghasilkan nilai ramalan yang akurat
  2. Perhitungan yang menggunakan arithmatic mean selalu sensitif terhadap data ekstrim/outlier
  3. Hanya bisa menghasilkan peramalan yang berdasarkan garis lurus

Cara membuat fungsi Semi Average di JS:

pivots = array => chunk(array, array.length/2).map(mean)

trend = array => (pivots(array)[1] - pivots(array)[0]) / (array.length/2)

semiAvg = array => withAs(
pivots(array)[0], anchor => array.map(
(i, j) => anchor + (trend(array) * j -
array.findIndex(k => k === anchor)
)
)
)

semiAvg([1, 2, 3, 4, 5, 6, 7, 8, 9, 10])
// get [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

pivots itu adalah fungsi yang ketika diberikan sebuah deret angka maka akan membagi 2 panjang data tersebut dan memberikan 2 angka yang merupakan nilai tengah dari pembagian deret tersebut. Sebagai contoh, ketika dipanggil pivots([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]) maka JS akan mengembalikan [3, 8]. Hal ini masuk akal karena bila deret angka tersebut dibagi jadi 2 kelompok 1-5 dan 6-10 maka nilai tengah masing-masing adalah 3, dan 8.

trend itu adalah fungsi yang ketika diberikan sebuah deret angka, maka akan mencari selisih nilai grup pertama dan kedua lalu membaginya dengan panjang setengah deret. Kenapa disebut trend? Karena angka yang keluar adalah perkiraan statistik seberapa besar kenaikan nilai pada setiap suksesi/urutan. Sebagai contoh, ketika dipanggil trend([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]) maka JS akan mengembalikan nilai 1. Artinya menurut statistik nilai peningkatan deret tersebut adalah 1 untuk setiap urutannya. Hal ini masuk akal, karena bisa kita lihat angka manapun dalam deret tersebut adalah lebih besar 1 poin dari nilai sebelumnya.

semiAvg itu adalah sebuah fungsi yang ketika diberikan sebuah deret angka, ia akan mengembalikan sebuah deret angka baru yang peningkatan setiap urutannya tergantung pada nilai trend yang diperoleh secara statistik. Pada contoh semiAvg diatas didapati bahwa dengan memanggil semiAvg([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]) akan mengembalikan [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]. Hal ini wajar karena secara statistik JS menemukan angka trend sebesar 1 dan membuat deret baru dengan nilai trend tersebut. Hal yang berbeda akan terjadi ketika kita memanggil semiAvg([1, 2, 3, 4, 5, 5, 7, 8, 9, 10]), silahkan dicoba sendiri.

Hanya membuat fungsi yang bisa memberikan kita deret angka baru yang berdasarkan matematika statistik mungkin kurang begitu bermanfaat untuk prediksi data kedepan. Untuk itu kita perlu membuat fungsi baru dengan logika yang sama yang mampu mengizinkan kita untuk melakukan prediksi data kedepan.

semiAvgN = (array, next = 0) => withAs(
pivots(array)[0], anchor =>
makeArray(array.length + next).map(
(i, j) => anchor + (trend(array) * j -
array.findIndex(k => k === anchor)
)
)
)

semiAvgN([1, 2, 3, 4, 5, 6, 7, 8, 9, 10], 2)
// get [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]

semiAvgN adalah fungsi yang ketika diberikan sebuah deret angka berikut dengan panjang prediksi, maka akan mengembalikan sebuah deret angka baru yang setiap nilainya adalah hasil perhitungan berdasarkan angka trend sepanjang deret angka bahan ditambah dengan panjang prediksi. Coba perhatikan contoh pemanggilan semiAvgN diatas. Ketika diberikan deret angka 1-10 dengan increment/peningkatan 1, dan kita minta prediksi 2 suksesi setelahnya, komputer mengembalikan 12 deret angka yang merupakan hasil prediksi dengan menggunakan nilai trend. Nilai 11 dan 12 yang ditambakan pada deret angka baru masuk akal karena nilai trend yang ada di data bahan adalah 1.

Sekarang coba Anda panggil semiAvgN([........], n) dengan deret angka bebas sesuai keinginan Anda dan minta komputer untuk menghasilkan prediksi sepanjang n di masa depan. Bagaimana deret angka baru yang Anda temukan? Silahkan bereksperimen.

Kelebihan Metode Semi Average:

  1. Mudah dimengerti dan menggunakan matematika sederhana
  2. Bagus untuk data yang relatif linear dengan dispersi yang kecil
  3. Lebih mendekati hasil prediksi dengan metode Freehand

Kekurangan Metode Semi Average:

  1. Hanya berlaku untuk pola linear, jelek untuk pola lainnya
  2. Pada data dengan dispersi besar, nilai prediksi akan banyak meleset
  3. Terlalu sensitif terhadap data outlier, rentan meleset

Bedah Kode: Semi Average

Silahkan baca bagian ini bagi yang berminat untuk mempelajari detail kode JSnya:

pivots = array => chunk(array, array.length/2).map(mean)
// ^ fungsi ini menerima sebuah himpunan angka
// ^ patahkan himpunan jadi 2
// ubah deretnya jadi mean ^

trend = array => (pivots(array)[1] - pivots(array)[0]) / (array.length/2)
// ^ fungsi ini menerima sebuah himpunan angka
// ^ mean himpunan kedua dikurangi dengan
// ^ mean himpunan pertama
// dibagi dengan setengah panjang himpunan ^

semiAvg = array => withAs(
// ^ terima sebuah himpunan angka
pivots(array)[0], anchor => array.map(
// ^ mean patahan pertama disebut sebagai anchor
// urai himpunan bahan ^
(i, j) => anchor + (trend(array) * j -
array.findIndex(k => k === anchor)
) // terjemahkan rumus jadi kode JS
)
)

semiAvgN = (array, next = 0) => withAs(
// ^ terima sebuah deret angka
// ^ terima angka prediksi
// kalau tidak ada, anggap 0
pivots(array)[0], anchor =>
// ^ carikan angka pivot pertama
// dan anggap sebagai ^ anchor
makeArray(array.length + next).map(
// ^ buat deret baru sepanjang data awal
// ditambah panjang deret yang ^ diharapkan
// dan ubah setiap angka itu ^
(i, j) => anchor + (trend(array) * j -
// menjadi nilai ^ anchor ditambah nilai trend
// dikali dengan index dan dikurangi ^ dengan
array.findIndex(k => k === anchor)
// cari index dimana nilai k sama dengan anchor
)
)
)

6.3. Least Square

Gambar: Contoh garis Least Square diantara sebuah set data

Least Square kalau diterjemahkan secara langsung artinya kuadrat terkecil. Lalu apa hubungannya kuadrat terkecil untuk prediksi? Disebut demikian karena kalau misalnya kita tarik sebuah garis dengan metode Freehand yang paling mewakili pola linier sebuah data maka nilai kuadrat selisih antara data asli dan prediksi adalah yang paling kecil, singkat kata, adalah yang paling akurat. Kalau metode Freehand adalah cara manusia menggunakan intusinya untuk membuat pola, Least Square adalah cara yang kita ajarkan kepada mesin untuk melakukan hal yang kurang lebih sama dengan cara manusia tersebut. Berikut ini adalah formula matematis Metode Least Square:

Rumus: Least Square untuk 2 variabel

Kalau formula tersebut diterjemahkan menjadi kode JS maka bentuknya akan seperti kode dibawah ini:

isOdd = num => num % 2

middleIndex = length => isOdd(length) ?
makeArray(length).map((i, j) => j + 1 - Math.round(length / 2)) : [
...makeArray(length/2).reduce((res, inc) => [-1 + (-2*inc), ...res], []),
...makeArray(length/2).reduce((res, inc) => [...res, 1 + (2 * inc)], [])
]

leastSquareEqu = array => withAs(
middleIndex(array.length), index => ({
a: mean(array),
b: sum(array.map((i, j) => i * index[j]))
/ sum(index.map(pow(2)))
}))

leastSquarePred = (array, next) => withAs({
index: middleIndex(array.length),
equation: leastSquareEqu(array)
}, ({index, equation}) => [
...index.map(i => equation.a + equation.b * i),
...makeArray(next).map(i =>
equation.a + equation.b * (last(index)+i+1)
)
])

leastSquareEqu([170, 190, 225, 250, 325])
// get {a: 232, b: 37}

leastSquarePred([170, 190, 225, 250, 325], 3)
// get [158, 195, 232, 269, 306, 343, 380, 417]

isOdd adalah fungsi yang ketika diberikan sebuah angka maka akan mengembalikan nilai benar bila angka tersebut adalah ganjil (ketika dibagi 2 hasilnya bukan bilangan bulat). Sementara ketika diberikan angka genap (bisa dibagi 2 dengan hasil bilangan bulat) akan mengembalikan nilai Salah. Anda boleh mencari contoh fungsi isOdd di internet dan kemungkinan akan mendapati contoh yang jauh lebih kompleks, sementara pada buku ini fungsi isOdd sangat pendek dan sederhana.

middleIndex adalah fungsi yang ketika diberikan sebuah angka maka akan memberikan sebuah deret angka yang isinya adalah nilai berurut tapi mulainya dari tengah. Semakin jauh ke kanan sebuah angka maka akan semakin besar nilai positifnya. Semakin jauh ke kiri sebuah angka maka akan semakin besar nilai negatifnya. Ada perbedaan set angka yang dikembalikan fungsi middleIndex tergantung panjang deret bahan yang diberikan itu ganjil atau genap. Bila angka yang diberikan adalah ganjil maka angka tengah yang mengawalinya adalah 0 dan kelanjutannya di sebelah kanan bernilai +1 dan seterusnya, begitu pula kelanjutan di sebelah kiri bernilai -1 dan seterusnya. Hal berbeda terjadi ketika kita berikan angka genap. Karena tidak ada nilai tengah tunggal, maka yang menjadi nilai tengah adalah -1 dan +1 sekaligus, dan setiap kelanjutan ke kanan adalah penambahan +2 dan kelanjutan deret sebelah kiri sebesar -2. Silahkan coba sendiri panggil middleIndex(n) dimana n adalah angka bebas yang boleh Anda tentukan sendiri dan lihat deret angkanya.

leastSquareEqu adalah fungsi yang ketika diberikan sebuah deret angka yang berasal dari data real maka akan mengembalikan nilai a dan b, dimana a adalah nilai konstanta dan b adalah nilai koefisien dari X. Jadi kalau pada buku pelajaran statistik lainnya Anda biasa melihat bahwa persamaan Least Square itu adalah Y = a + bX maka nilai yang dikeluarkan oleh fungsi ini adalah untuk mencari nilai a dan b itu. Nilai a sederhananya adalah mean dari deret tersebut, sementara nilai b adalah hasil pembagian P dan Q, dimana P adalah jumlah perkalian data real dan nilai middleIndex sesuai posisi relatif data tersebut, sementara Q adalah penjumlahan dari pangkat dua semua nilai middleIndex. Karena perhitungan nilai a hanya menggunakan mean makanya a menjadi konstan terlepas dari manapun posisi data yang sedang dihitung/diprediksi. Sementara nilai b perhitungannya tergantung pada posisi relatifnya terhadap middleIndex, makanya nilai b merupakan koefisien bagi X. Bagi yang berminat silahkan pelajari lebih dalam tentang logika fungsi Least Square di sumber lain seperti buku Matematika Statistik, Wikipedia, atau YouTube.

leastSquarePred adalah fungsi yang ketika diberikan sebuah deret angka maka akan memberikan sebuah deret angka baru yang merupakan hasil prediksi statistik secara matematis, ditambah dengan panjang prediksi yang kita harapkan. leastSquarePred ini memanfaatkan fungsi leastSquareEqu yang telah kita bahas tadi dan membuat deret angka baru yang merupakan hasil prediksi berikut dengan nilai prediksi kedepan yang kita tentukan. Pada contoh kode diatas dapat kita lihat hasil pemanggilan fungsi leastSquarePred ketika diberikan data dan nilai prediksi 3 periode kedepan. Dapat dilihat bahwa data kita yang awalnya hanya deret sepanjang 5 kini telah menjadi deret sepanjang 8 (5 prediksi dasar dan 3 prediksi kedepan). Jika Anda perhatikan dengan baik, dari contoh pemanggilan leastSquareEqu kita mendapati nilai 232 sebagai konstanta dan 37 sebagai koefisien variabel X. Dari persamaan ini kita tahu bahwa nilai tengah dari deret data asli adalah 232 dan setiap kali data bergerak ke kanan maka akan bertambah 37 poin dan akan berkurang 37 poin pada deret sebelah kiri. Semakin jauh data ke kanan maka akan terus ditambah dengan kelipatan 37 poin. Karena pertambahan nilai sebesar 37 poin yang tidak berubah inilah makanya persaman ini disebut linier.

Sekarang silahkan Anda coba karang sendiri deret angka (bebas berapa digit) dengan pola peningkatan atau penurunan yang linier (tidak harus tepat) dan coba panggil dengan leastSquarePred berikut dengan panjang prediksi yang diharapkan. Bila Anda ingin memahami mengapa komputer mengembalikan deret angka seperti itu, coba panggil data yang sama dengan leastSquareEqu, maka akan keluar nilai a yang merupakan konstanta dari data karangan Anda, dan nilai koefisien b variabel X yang secara statistik diprediksikan oleh komputer Anda. Jangan marah/kecewa bila hasil prediksi relatif jauh dari data real, bisa jadi data karangan Anda memang tidak berpola linear.

Kelebihan Metode Least Square:

  1. Hasil prediksi relatif lebih akurat daripada 2 metode sebelumnya
  2. Bisa sekalian tahu konstanta dan koefisien X yang membentuk garis liniernya

Kekurangan Metode Least Square:

  1. (Bisa jadi) kalah akurat dibanding regresi linear
  2. Tetap over-sensitif terhadap data outlier yang bisa merusak hasil prediksi

Bedah Kode: Least Square

Silahkan baca bagian ini bagi yang berminat untuk mempelajari detail kode JSnya:

isOdd = num => num % 2
// ^ terima sebuah angka
// ^ kalau dibagi 2, berapa sisanya?
// kalau hasilnya 0, berarti dia genap
// kalau bersisa artinya ganjil

isOdd(2) // 2 % 2 = 0, karena tidak bersisa maka dianggap False
// kalau bukan bukan Odd (ganjil), berarti Even (genap)
isOdd(3) // 5 % 2 = 1, karena bersisa maka dianggap True
// kalau hasilnya true, berarti benar dia Odd (ganjil)

middleIndex = length => isOdd(length) ?
// ^ sebutkan mau panjang berapa himpunannya
// ^ kalau dia ganjil, maka..
makeArray(length).map((i, j) => j + 1 - Math.round(length / 2)) : [
// ^ buat deret baru yang isinya angka indeks + 1
// dan dikurangi setengah panjang deret ^
// tapi kalau hasilnya genap, maka ^
...makeArray(length/2).reduce((res, inc) => [-1 + (-2*inc), ...res], []),
// ^ buat himpunan kiri yang isinya -1 ditambah -2 kali inc
...makeArray(length/2).reduce((res, inc) => [...res, 1 + (2 * inc)], [])
// ^ buat himpunan kanan yang isinya 1 ditambah 2 kali inc
]

leastSquareEqu = array => withAs(
// ^ terima sebuah himpunan
middleIndex(array.length), index => ({
// ^ buat middleIndex sepanjang array.length
// sebut saja dia index ^
a: mean(array),
// ^ rata-rata nilai himpunan/deret
b: sum(array.map((i, j) => i * index[j]))
// ^ urai himpunan, dan i * index[j]
// ^ lalu jumlahkan
/ sum(index.map(pow(2)))
// ^ bagi dengan penjumlahan dari
// ^ setiap angka index di-pangkat dua-kan
}))

leastSquarePred = (array, next) => withAs({
// ^ terima sebuah deret angka
// ^ dan panjang prediksi berikutnya
index: middleIndex(array.length),
// ^ buat sebuah deret middleIndex sepanjang deret asli
equation: leastSquareEqu(array)
// ^ carikan persamaan leastSquare dari deret itu
}, ({index, equation}) => [
// ^ 2 data ini siap untuk dipakai
...index.map(i => equation.a + equation.b * i),
// ^ ubah setiap angka index menjadi implementasi persamaan
...makeArray(next).map(i =>
// ^ buat deret baru sepanjang angka 'next'
equation.a + equation.b * (last(index)+i+1)
// ^ dan setiap angka itu diimplementasikan ke persamaan
)
])

6.4. Parabolic Trend

Gambar: Contoh kurva parabolik pada pergerakan harga saham

Semua fungsi/persamaan yang telah kita bahas sebelumnya hanya cocok untuk menghadapi situasi dimana suatu set data memiliki pola yang liner/lurus. Ketika kita dihadapkan pada data yang berpola tidak-lurus, bengkok, dan kurva, semua alat yang diatas sama sekali tidak bisa dipakai. Kalau dipaksakan justru akan merusak hasil prediksi dan mengantarkan kita ke kesimpulan yang salah. Lalu bagaimana bila sebuah data ketika dipetakan dalam grafik memiliki pola yang terlihat berbentuk kurva seperti parabola? Adakah cara statistik yang bisa digunakan untuk melakukan prediksi pada set data yang seperti itu? Jawabannya ada, kita akan menggunakan Parabolic Trend. Parabolic Trend adalah formula statistik yang memungkinkan ahli statistik untuk mencari sebuah kurva trend (kurva, bukan garis lagi ya) yang paling akurat mendekati pola yang ada pada data. Tanpa mengandalkan formula Parabolic Trend sekalipun sebenarnya bisa saja kita menggunakan metode Freehand lagi untuk membuat kurva trend sendiri dengan mengandalkan intuisi dan pengalaman, sayangnya hal tersebut tidak reliabel dan dapat dipertanggungjawabkan secara ilmiah. Serupa dengan pola pikir Least Square sebelumnya, Parabolic Trend adalah cara kita mengajarkan kepada komputer bagaimana mencari sebuah kurva trend yang paling mendekati pola kurvatur pada data tanpa bantuan manusia. Dari sumber internet, kita peroleh bahwa formula matematis dari metode Parabolic Trend ini adalah seperti pada gambar berikut:

Rumus: Kurva trend Parabolik

Yang jika diterjemahkan menjadi kode JS maka akan menjadi seperti berikut:

parabolicTrend = array => withAs(
middleIndex(array.length), index => withAs({
SY : sum(array), length: array.length,
SXY : sum(index.map((i, j) => array[j] * i)),
SX2Y: sum(index.map((i, j) => array[j] * i * i)),
SX2 : sum(index.map(pow(2))),
SX4 : sum(index.map(pow(4))),
}, ({length, SY, SXY, SX2Y, SX2, SX4}) => ({
a: (length * SX2Y - SX2 * SY) /
(length * SX4 - pow(2)(SX2)),
b: SXY / SX2,
c: (SY * SX4 - SX2Y * SX2) /
(length * SX4 - pow(2)(SX2))
}))
)

parabolicTrend([12, 16, 19, 21, 22])
// get {a: -0.5, b: 2.5, c: 19}
// equal to -0.5X^2 + 2.5X + 19

parabolicTrend([30, 25, 21, 18, 16])
// get {a: 0.5, b: -3.5, c: 21}
// equal to -0.5X^2 + 2.5X + 19

parabolicTrendPred = (array, next) => withAs({
index: middleIndex(array.length),
equation: parabolicTrend(array)
}, ({index, equation}) => [
...index.map(i =>
equation.a * i * i +
equation.b * i +
equation.c
),
...makeArray(next).map(i =>
equation.a * pow(2)(1 + i + last(index)) +
equation.b * (1 + i + last(index)) +
equation.c
)
])

parabolicTrendPred([12, 16, 19, 21, 22], 5)
// get [12, 16, 19, 21, 22, 22, 21, 19, 16, 12]
// [-----raw data-----|-----predicted-----]

parabolicTrendPred([30, 25, 21, 18, 16], 5)
// get [30, 25, 21, 18, 16, 15, 15, 16, 18, 21]
// [-----raw data-----|-----predicted-----]

parabolicTrend adalah fungsi yang ketika diberikan sebuah set data maka akan mengembalikan 3 nilai (a, b, dan c). Bila Anda masih ingat bagaimana bentuk persamaan kuadrat, hampir selalu polanya adalah aX^2 + bX + c. Dimana a adalah koefisien dari x kuadrat, b koefisien x, dan c tanpa koefisien. Tapi secara matematis juga bisa dibaca sebagai aX^2 + bX^1 + cX^0, ingat bahwa variabel yang dipangkatkan 1, sama saja seperti dikali dengan 1, tapi ketika dipangkatkan dengan 0 maka variabel tersebut dianggap setara dengan angka 1. Maka pada fungsi ini, nilai a, b, dan c yang dikeluarkan adalah setara dengan konsep yang sudah dijelaskan tadi. Pada contoh kode diatas ada pemanggilan parabolicTrend dengan set data [12, 16, 19, 21, 22]. Bisa kita lihat disini bahwa data tersebut terus membesar di sebelah kanan tapi tingkat pembesarannya makin lama makin kecil. 12 ke 16 selisih 4, 16 ke 19 selisih 3, 19 ke 21 selisih 2, dan 21 ke 22 selisih 1. Kalau kita gambarkan deret angka ini ke sebuah kurva akan kita lihat bahwa ini memiliki garis yang menyerupai kurva. Maka benarlah adanya bahwa parabolicTrend adalah fungsi yang cocok untuk mengukurnya. Dari data yang tadi parabolicTrend mengembalikan sebuah set data yang isinya {a: -0.5, b: 2.5, c: 19} artinya koefisien X^2 adalah -0.5, koefisien X adalah 2.5, dan konstantanya adalah 19. Cara bacanya begini, menurut statistik, setiap kali X bergerak ke kiri/kanan maka Y akan bergerak naik/turun sesuai dengan hasil perhitungan X ketika dimasukkan ke persamaan kuadrat tadi. Sebagai contoh:

Y(-2) = -0.5*-2*-2 + 2.5*-2 + 19 = 12
Y(-1) = -0.5*-1*-1 + 2.5*-1 + 19 = 16
Y(0) = -0.5*0*0 + 2.5*0 + 19 = 19
Y(1) = -0.5*1*1 + 2.5*1 + 19 = 21
Y(2) = -0.5*2*2 + 2.5*2 + 19 = 22
Y(3) = -0.5*3*3 + 2.5*3 + 19 = 22
Y(4) = -0.5*4*4 + 2.5*4 + 19 = 21
Y(5) = -0.5*5*5 + 2.5*5 + 19 = 19
Y(6) = -0.5*6*6 + 2.5*6 + 19 = 16
Y(7) = -0.5*7*7 + 2.5*7 + 19 = 12

Itu adalah contoh hasil prediksi data bila kita lakukan perhitungan secara manual (tangan/kalkulator). Dapat kita lihat bahwa ketika kita masukkan deret indeks dari -2 hingga 7 kita mendapati deret nilai Y memiliki pola yang kurvatur, dalam kasus ini, naik hingga 22, tidak bertambah lagi, lalu turun kebawah lagi. Kenapa bisa demikian? Karena dari data yang kita berikan, menurut statistik data tersebut memiliki pola kurvatur dimana nilai naik dengan pertumbuhan yang melambat lalu akan kembali menurun setelah nya, makanya muncul angka -0.5. Bila Anda menggunakan set data yang lain dengan pola yang menurun dengan trend yang melambat, maka kemungkinan kurva tersebut akan berbalik keatas seperti pada contoh dengan data [30, 25, 21, 18, 16]. Bisa dilihat bahwa data semakin ke kanan semakin menurun tapi dengan tingkat penurunan yang semakin melambat. 30 ke 25 selisih 5, 25 ke 21 selisih 4, dan seterusnya. Bila Anda jeli melihat kedua contoh tersebut, Anda akan menyadari bahwa ketika X = 0, posisinya selalu berada di tengah data mentahnya. Kenapa? Karena perhitungan parabolicTrend menggunakan middleIndex, sehingga ketika kita minta komputer untuk mengeluarkan hasil prediksi, yang dijadikan titik pangkalnya adalah pertengahan data mentahnya.

Kelebihan Metode Parabolic Trend:

  1. Sangat cocok untuk memprediksi pola yang bentuknya kurvatur

Kekurangan Metode Parabolic Trend:

  1. Hanya bisa menghasilkan persamaan dengan kuadrat pangkat 2
  2. Hasil tidak akurat bila bentuk kurvanya tidak normal (berbentuk bel)

Bedah Kode: Parabolic Trend

Silahkan baca bagian ini bagi yang berminat untuk mempelajari detail kode JSnya:

parabolicTrend = array => withAs(
// ^ berikan sebuah deret angka
middleIndex(array.length), index => withAs({
// ^ buat sebuah deret middleIndex sepanjang deret asli
// lalu sebut saja dia 'index' ^
SY : sum(array), length: array.length,
// ^ penjumlahan deret asli
// ^ panjang deret aslinya
SXY : sum(index.map((i, j) => array[j] * i)),
// ^ urai setiap angka index
// ambil nilai di array sesuai index j ^
// kalikan dengan nilai yg diurai ^
// ^ lalu jumlahkan
SX2Y: sum(index.map((i, j) => array[j] * i * i)),
// ^ urai setiap angka index
// ambil nilai di array sesuai index j ^
// kalikan dengan i pangkat 2 ^
// ^ baru jumlahkan
SX2 : sum(index.map(pow(2))),
// ^ pangkat 2-kan setiap angka index
// ^ lalu jumlahkan
SX4 : sum(index.map(pow(4))),
// ^ pangkat 4-kan setiap angka index
// ^ lalu jumlahkan
}, ({length, SY, SXY, SX2Y, SX2, SX4}) => ({
// ^ siapkan semua bahan masakan
a: (length * SX2Y - SX2 * SY) /
(length * SX4 - pow(2)(SX2)),
// ^ buat nilai a sesuai rumus untuk X^2
b: SXY / SX2,
// ^ buat nilai b sesuai rumus untuk X^1
c: (SY * SX4 - SX2Y * SX2) /
(length * SX4 - pow(2)(SX2))
// ^ buat nilai c sesuai rumus untuk X^0
}))
)

parabolicTrendPred = (array, next) => withAs({
// terima data bahan ^
// dan panjang prediksi ^ yg diinginkan
index: middleIndex(array.length),
// ^ buat deret middleIndex sepanjang data bahan
equation: parabolicTrend(array)
// ^ carikan dulu persamaannya
}, ({index, equation}) => [
// ^ siapkan bahan-bahannya
...index.map(i =>
// ^ ubah setiap angka index menjadi..
equation.a * i * i +
// ^ nilai a dikali i pangkat 2
equation.b * i +
// ^ tambah nilai b dikali i
equation.c
// ^ dan ditambah nilai c
),
...makeArray(next).map(i =>
// ^ disambung dengan
// ^ sebuah deret baru sepanjang next, yg isinya
equation.a * pow(2)(1 + i + last(index)) +
// ^ nilai a dikali pangkat 2 dari 1+i+index terakhir
equation.b * (1 + i + last(index)) +
// ^ nilai b dikali 1+i+index terakhir
equation.c
// ^ ditambah dengan konstanta c
)
])

6.5. Exponential Trend

Gambar: Contoh kurva eksponensial

Ada jenis kurva tertentu yang bentuknya bukan garis kurva terbuka ke atas ataupun terbuka ke bawah, kurva jenis ini bengkoknya ke arah kanan dan ujungnya tidak mencapai infinity tapi malah secara terus menerus mendekati sebuah nilai Y (tapi tidak pernah menyentuh). Dalam matematika, kurva ini disebut dengan kurva eksponensial, yang artinya variabel X menjadi pangkat bagi konstanta e (angka Euler) yang sering disebut sebagai konstanta natural di alam semesta, silahkan searching sendiri tentang “Euler’s Number”. Ada banyak sekali contoh kurva yang memiliki pola eksponensial seperti ini. Salah satu contohnya adalah kecepatan kereta api, mulai dari yang mode uap, yang mode listrik, yang mode MagLev (magnetic levitation, mengapung dengan elektromagnetik), hingga prototip HyperLoop yang menggunakan ruang vakum. Kesemua jenis kereta api tersebut walau kecepatan maksimalnya terus meningkat seiring perkembangan teknologi tapi trend peningkatannya semakin lambat. Walaupun teknologi permesinan kereta api semakin canggih, kemampuannya akan mencapai puncak kecepatan tertentu yang akan semakin sulit menembus rekor tersebut. Tidak hanya pada bidang ilmu alam, pada bidang ilmu ekonomi hal ini juga terjadi. Bayangkan bila Anda yang sedang kehausan diberikan air minum, kepuasannya akan melonjak tinggi, tapi semakin banyak Anda diberi air minum, Anda akan mulai merasa bosan dan kepuasan Anda minum air tidak dapat ditambah lagi. Lalu kenapa tidak gunakan fungsi parabolicTrend yang sudah dipelajari sebelumnya kalau memang sama-sama kurva juga? Karena parabolicTrend hanya bisa dipakai bila kurva tersebut terbuka ke atas atau ke bawah, sementara pada kurva yang terbukanya ke samping, bisa jadi metode eksponensial lebih cocok.

Dalam ilmu matematika statistik, formula yang digunakan untuk mengubah data mentah menjadi persamaan eksponensial adalah seperti ini:

Rumus: Kurva trend eksponensial

Dan bila dikonversi menjadi kode JS, maka akan jadi seperti ini:

euler = 2.718281828459045
// the mysterious natural number

expoTrend = arr => withAs(middleIndex(arr.length), index => ({
a: pow(sum(arr.map(Math.log)) / arr.length)(euler),
b: pow((
sum(index.map((i, j) => i * Math.log(arr[j]))) /
powSum(2, index)
) - 1)(eulr)
}))

expoTrend([59, 50, 44, 38, 33, 28, 23]) // get {a: 37.52, b: 0.8584}

expoTrendPred = (equ, idx) => equ.a * pow(idx)(equ.b)

middleIndex(7).map(
i => expoTrendPred(
{a: 37.52, b: 0.8584}, i
)
) // get [59.31, 50.92, 43.7, 37.52, 32.2, 27.64, 23.73]

euler adalah sebuah konstanta bernilai desimal yang irasional (semakin dihitung, semakin panjang desimalnya tanpa ada tanda-tanda pola untuk berhenti di 0). Disebut euler karena yang mempeloporinya adalah seorang ilmuan Prancis bernama Euler, tapi bukan dia yang pertama menemukannya. Bangsa-bangsa kuno seperti China dan Arab yang jauh lebih tua juga punya arsip tersendiri tentang konstanta natural ini, namun untuk mempermudah komunikasi, kita sebut saja Euler, atau cukup 'e'. Bagi yang berminat, silahkan coba cari tahu tentang natural number ini dan saksikan bagaimana konstanta ini bisa muncul di banyak tempat secara matematis, mulai dari bunga, bintang, sampai ke perhitungan bunga bank.

expoTrend adalah fungsi yang ketika menerima sebuah deret angka maka ia akan mengembalikan 2 buah nilai, yaitu a dan b. Nilai a adalah koefisien yang akan menjadi faktor perkalian di depan konstanta euler, sementara nilai b menjadi nilai pangkat bagi konstanta euler. Berbeda dengan parabolicTrend sebelumnya yang dominan menggunakan operasi aritmatika (kali, bagi, tambah, kurang), pada expoTrend ini justru menggunakan fungsi perpangkatan dan logaritma untuk menghasilkan kedua koefisien tersebut. Ketika expoTrend dipanggil dengan data [59, 50, 44, 38, 33, 28, 23]maka ia akan mengembalikan nilai {a: 37.52, b: 0.8584}. Artinya, menurut statistik, persamaan eksponensial yang paling cocok untuk menjelaskan dan memprediksi data mentah yang Anda berikan adalah 37.52*(e^0.8584X).

expoTrendPred adalah fungsi yang ketika menerima kedua koefisien a dan b, ditambah dengan indeks prediksi yang kita minta, maka ia akan mengembalikan hasil prediksi untuk angka indeks tersebut. Sekarang mari kita coba satu per satu nilai indeks untuk melihat seberapa jauh atau dekat hasil prediksi statistik terhadap data mentah yang kita berikan:

expoTrendPred(expoTrend([59, 50, 44, 38, 33, 28, 23]), -3) // get 59.32
expoTrendPred(expoTrend([59, 50, 44, 38, 33, 28, 23]), -2) // get 50.92
expoTrendPred(expoTrend([59, 50, 44, 38, 33, 28, 23]), -1) // get 43.71
expoTrendPred(expoTrend([59, 50, 44, 38, 33, 28, 23]), 0) // get 37.52
expoTrendPred(expoTrend([59, 50, 44, 38, 33, 28, 23]), 1) // get 32.21
expoTrendPred(expoTrend([59, 50, 44, 38, 33, 28, 23]), 2) // get 27.65
expoTrendPred(expoTrend([59, 50, 44, 38, 33, 28, 23]), 3) // get 23.74

Coba kita bandingkan hasil prediksi dengan data dasar kita [59, 50, 44, 38, 33, 28, 23]. Dapat dilihat ada kedekatan antara hasil prediksi dengan data dasar kita bukan? Bahkan ketika kita sebutkan angka 3 sebagai angka indeks untuk diprediksi, keluar angka 23.74, artinya kalau kurva eksponensial ini berlanjut ke kanan, maka pada indeks ke-3 angka yang akan keluar seharusnya adalah 23.74. Bila Anda perhatikan dengan baik, ketika Anda coba prediksi nilai Y ketika indeks 0 maka yang akan keluar adalah 37.52, dimana angka ini adalah cenderung mendekati titik tengah dari data dasar yang Anda gunakan. Sekarang mari kita coba lanjut eksperimen lagi, bagaimana kalau kita ingin tahu prediksi angka pada indeks ke-100 di depannya:

expoTrendPred(expoTrend([59, 50, 44, 38, 33, 28, 23]), 100) // get 0.00000883

Kita menggunakan angka 100 untuk memprediksi jauh ke depan, tapi kenapa angka yang dikembalikan tidak membesar, menjadi minus, atau 0 absolut? Malah ia terus mengecil mendekati 0 tanpa menyentuh 0. Bila kita coba memprediksi indeks ke 10 ribu atau 1 juta indeks kedepan, ia akan terus mendekati 0 dengan desimal 0 yang banyak tapi tidak akan pernah betul-betul 0, itulah kurva eksponensial.

Kelebihan Metode Exponential Trend:

  1. Sangat cocok untuk data yang memiliki pola eksponensial
  2. Bisa menghasilkan persamaan eksponensial yang mewakili pola tersebut

Kekurangan Metode Exponential Trend:

  1. Hanya cocok untuk data yang memiliki pola eksponential
  2. Masih over-sensitive terhadap data outlier yang membuat prediksi meleset

Bedah Kode: Exponential Trend

Silahkan baca bagian ini bagi yang berminat untuk mempelajari detail kode JSnya:

expoTrend = arr => withAs(middleIndex(arr.length), index => ({
// ^ terima sebuah deret
// buat sebuah deret middleIndex ^ sepanjang deret bahan
// lalu sebut saja dia index ^
a: pow(sum(arr.map(Math.log)) / arr.length)(euler),
// ^ ubah setiap angka arr jadi nilai Math.log
// ^ jumlahkan lalu bagi dengan ^ panjang arr
// lalu jadikan dia pangkat dari angka euler ^
b: pow(
sum(index.map((i, j) => i * Math.log(arr[j]))) /
// ubah setiap angka indeks jadi perkalian i dengan
// nilai Math.log dari nilai arr pada index j
sum(index.map(pow(2)))
// ^ pangkat 2-kan setiap angka indeks lalu jumlahkan
)(euler)
// jadikan angka tadi sebagai nilai pangkat bagi euler
}))

expoTrendPred = (equ, idx) => equ.a * pow(idx)(equ.b)
// ^ terima persamaan dan nilai index diinginkan
// sesuaikan dengan rumus aslinya ^

6.6. Cyclical Trend

Gambar: Contoh pola siklus pada data

Anda nanti akan menemukan kondisi dimana sebuah data bukannya berpola terbuka ke atas/bawah, ke samping secara eksponensial/logaritmik, melainkan naik dan turun secara kontiniu (terus menerus) layaknya sebuah gelombang. Mari kita narasikan sebuah contoh sederhana yang datanya bisa jadi mirip dengan siklus gelombang ini. Anda pergi ke pasar pakaian dan mewawancarai seorang pedagang sarung dan sajadah. Jenis barang ini tentu bisa dibeli kapan saja oleh pelanggan, namun ada masa-masa tertentu dimana penjualan meningkat lebih dari biasanya dan turun lagi ketika masa tersebut berlalu. Anda bertanya kepada pedagang: “Bagaimana siklus penjualan sajadah, sarung dan baju koko di tempat bapak?”, “Ya, Alhamdulillah dek, kalau pas lagi musim idul fitri, lebaran haji, atau maulid nabi meningkat. Tapi kalau udah lewat mah ya turun lagi”. “Boleh saya lihat catatan riwayat penjualan bulanan bapak?”, “Mangga mangga”. Anda pun melihat tabel catatan penjualan dimana angka tersebut memang naik turun seiring perubahan waktu dan terutama ada pola siklus yang hampir serupa dalam setiap tahun tersebut. Andapun berpikir, apakah saya punya cara matematis statistik untuk mengetahui secara kuantitatif bagaimana bentuk siklus ini dalam deret angka? Jawabannya ada.

Cycle Trend adalah fungsi statistik yang bisa mengubah suatu set data mentah riwayat siklus perubahan. Data perubahan ini bisa apapun, tingkat kecerahan bulan dalam tempo 1 bulan/tahun, curah hujan, kesuburan dan masa panen tanaman, kesehatan ternak, sampai ke penjualan sarung (Joke: Penulis hampir gadai sarung demi buku ini). Kesemua contoh kasus tersebut memiliki ciri yang sama, ada masanya dia turun, naik, turun lagi, naik lagi. Bentuk rumus matematis statistik cycle trend adalah seperti ini:

Rumus: Cyclical trend

Yang jika diubah menjadi kode JS, maka menjadi seperti ini:

cycleTrend = arrays => withAs(
makeArray(arrays[0].length)
.map(i => sum(arrays.map(j => j[i]))),
sums => sums.map(i => i / mean(sums))
)

cycleTrend adalah fungsi yang ketika menerima suatu set lebih dari 1 deret angka, maka ia akan membuat 1 deret baru yang isinya adalah perkiraan rasio siklus peningkatan/penurunan data tersebut. Contohnya seperti kode berikut:

cycleTrend([
//[q1 q2 q3 q4]
[2, 3, 3, 4], // year 1
[3, 4, 4, 6], // year 2
[4, 4, 3, 5], // year 3
[4, 5, 5, 7] // year 4
])
// get [0.7878, 0.9696, 0.9090, 1.3333]
// [q1 q2 q3 q4 ]

Apa yang dilakukan oleh fungsi cycleTrend tersebut? Misalnya dalam periode 1 tahun (sebenarnya juga boleh hari, bulan, tahun, abad, yang penting konsisten) ada 4 kuartal (boleh juga pecahan berapapun, asal konsisten). Ia akan mencari rata-rata tiap kuartal dari tahun yang berbeda, menjumlahkannya, lalu membandingkannya lagi dengan rata-rata penjumlahan tersebut. Deret angka yang ia hasilkan adalah perkiraan rasio trend yang muncul pada setiap kuartal. Sebagai contoh, pada output diatas kita peroleh angka 0.78 pada kuartal I, dan 0.96 pada kuartal II. Artinya pada kuartal I penjualan sajadah bapak tadi adalah 78% dari rata-rata siklus, sementara pada kuartal II naik hingga ke 96%, puncak tertinggi ada di kuartal IV dengan 130% dari rata-rata penjualan. Dengan berbekal hasil output fungsi cycleTrend ini Anda bisa memperkirakan peningkatan/penurunan penjualan per kuartal dagangan bapak tersebut, dengan modal mengetahui nilai kuartal pertamanya.

Sebagai contoh, bila pada tahun berikutnya bapak tersebut mendapati penjualan di kuartal I nya adalah 5, maka nilai tengah siklus adalah 5/0.78 = 6.41. Artinya nilai tengah siklus bapak selama tahun berjalan tersebut adalah 6.41 (kuintal, misalnya). Maka untuk kuartal II kita tinggal kalikan saja nilai rasio pada kuartal II dengan nilai tengah siklus, 6.41 * 0.9696 = 6.215. Artinya kuartal II penjualan bapak tersebut diperkirakan 6.215, atau 1.215 kuintal lebih banyak dari kuartal I. Sementara pada kuartal IV, penjualan dagangan bapak tersebut diperkirakan 6.41 * 1.33 = 8.52 kuintal, tertinggi di tahun tersebut. Semua prediksi statistik ini hanya akan tepat bila siklus yang terjadi di lapangan konsisten dengan pola siklus yang terdapat pada data. Bila terjadi faktor lain yang mendadak mengubah semua kondisi tersebut, maka tentu hasil prediksi akan jauh dari realita, contoh: pandemi Covid-19.

Maka dengan berbekal hasil analisis statistik cycleTrend, Anda kembali ke bapak tersebut dan menunjukkan deret angka rasio berikut dengan perkiraan penjualan hingga kuartal IV tahun tersebut. "Makasih dek, tapi bapak jualan mah istikomah ajah. Mau naik ya sukur, kalau turun ya ekhlas". Setidaknya dengan ini Anda membuktikan bahwa statistik adalah kekuatan untuk memprediksi masa depan dengan tingkat akurasi tertentu.

Kelebihan Metode Cycle Trend:

  1. Cocok untuk menganalisa trend yang naik turun
  2. Bisa menghasilkan angka rasio untuk perkiraan kedepan
  3. Bisa pakai satuan apapun (tahun, abad) dengan partisi berapapun (harian, mingguan, kuartal)

Kekurangan Metode Cycle Trend:

  1. Sangat tergantung pada konsistensi siklus pada data dasar
  2. Data yang tidak normal atau outlier pada bahan akan merusak akurasi

Bedah Kode: Cycle Trend

Silahkan baca bagian ini bagi yang berminat untuk mempelajari detail kode JSnya:

cycleTrend = arrays => withAs(
// ^ terima sebuah deret
makeArray(arrays[0].length)
// ^ buat sebuah deret baru sepanjang kolom
.map(i => sum(arrays.map(j => j[i]))),
// ^ ubah setiap nilai jadi jumlah setiap kolom
sums => sums.map(i => i / mean(sums))
// ^ maka kita dapati sebuah deret baru yg isinya
// penjumlahan dari setiap kolom
// ^ ubah setiap angka itu menjadi
// i dibagi rata-rata dari sums
)

6.7. Rational Trend

Ada penantang baru di statistik yang klaim bahwa hasil prediksinya untuk data berpola siklus lebih akurat dari Cycle Trend, namanya Rational Trend. Kritik terhadap Cycle Trend adalah karena dianggap naif karena hanya mengandalkan rata-rata antar kuartal dan membaginya dengan mean. Maka untuk mencapai hasil prediksi yang lebih akurat, Rational Trend menggunakan perhitungan multi-stage (beberapa tahapan) yang bisa Anda lihat pada contoh kode di bawah. Formula statistik untuk perhitungan Rational Trend adalah seperti berikut:

Rumus: Rational Trend

Yang bila dikonversi menjadi kode JS bentuknya sebagai berikut:

ratioTrend = arrays => withAs(
middleIndex(arrays.length), index => ({
a: sum(arrays.map(sum)) / arrays.length,
b: sum(index.map((i, j) => i * sum(arrays[j]))) /
sum(index.map(pow(2)))
})
)

ratioTrend([
[2, 3, 3, 4],
[3, 4, 4, 6],
[4, 4, 3, 5],
[4, 5, 5, 7]
]) // get ({a: 16.5, b: 1.3}) yearly prediction

ratioTrendPred = arrays => withAs(ratioTrend(arrays), trend =>
chunk(middleIndex(arrays.length * arrays[0].length).map(i =>
(trend.a / 4) + (trend.b / 16 * i) // split to quartile
), 4)
)

ratioTrendPred([
[2, 3, 3, 4],
[3, 4, 4, 6],
[4, 4, 3, 5],
[4, 5, 5, 7]
]) /* get quartile prediction result [
[2.90625, 3.06874, 3.23125, 3.39375],
[3.55625, 3.71875, 3.88125, 4.04375],
[4.20625, 4.36875, 4.53125, 4.69375],
[4.85625, 5.01875, 5.18125, 5.34375]
] */

ratioTrendDiff = arrays => withAs(
ratioTrendPred(arrays).flat(), pred =>
chunk(arrays.flatMap(
(i, j) => i * 100 / pred[j]
), 4)
)

ratioTrendDiff([
[2, 3, 3, 4],
[3, 4, 4, 6],
[4, 4, 3, 5],
[4, 5, 5, 7]
]) /* how different: real vs pred [
[68.81, 97.75 , 92.84 , 117.86],
[84.35, 107.56, 103.05, 148.37],
[95.09, 91.55 , 66.20 , 106.52],
[82.36, 99.62 , 96.50 , 130.99]
]*/

ratioTrendSeason = arrays => withAs(ratioTrendDiff(arrays), diff =>
withAs(makeArray(4).map(i => mean(diff.map(j => j[i]))), averages =>
averages.map(i => i * 100 * 4 / sum(averages))
)
)

ratioTrendSeason([
[2, 3, 3, 4],
[3, 4, 4, 6],
[4, 4, 3, 5],
[4, 5, 5, 7]
]) // get [83.20, 99.78, 90.24, 126.77]

ratioTrend adalah fungsi yang ketika menerima sebuah set data yang mengandung beberapa deret angka (kuartal) maka akan menghasilkan sepasang konstanta (a dan b) yang akan menjadi bahan perhitungan untuk prediksi fungsi berikutnya. Nilai a tersebut adalah hasil penjumlahan semua angka bahan yang Anda berikan, dibagi dengan banyak deret angka bahan yang Anda berikan, pada contoh ini 4(baris ke bawah). Nilai b tersebut adalah jumlah hasil perkalian antara nilai middleIndeks dengan jumlah tiap baris, lalu dibagi lagi dengan kuadrat middleIndeks. Pada contoh diatas, dari data bahan yang digunakan kita mendapati nilai {a: 16.5, b: 1.3}. Angka ini mungkin tidak dapat dipahami secara langsung, karena harus diolah kembali oleh fungsi berikut dibawah ini.

ratioTrendPred adalah fungsi yang ketika menerima sebuah set dengan beberapa deret angka maka akan mengembalikan suatu set data baru dengan jumlah baris dan kolom yang sama, tapi isinya adalah murni prediksi statistik. Pada contoh data diatas, kita bisa melihat bahwa hasil prediksi kuartil yang dikeluarkan relatif mirip dengan data bahan yang kita berikan. Dari sini Anda bisa berintuisi seberapa akurat model statistik yang dihitung mendekati pola data real.

ratioTrendDiff adalah fungsi yang ketika diberikan sebuah set data, maka ia akan mengembalikan sebuah set baru dengan jumlah baris dan kolom yang sama dengan data bahan, tapi isinya adalah persentase seberapa berbeda antara data real dengan data prediksi yang dikeluarkan oleh model statistik. Pada contoh kasus ini, pada baris 1 dan kolom 1 kita dapati nilai 68.81 yang artinya ada perbedaan 68% antara data real(=2) dan data prediksi(=2.90625). Fungsi ini bukan untuk menghasilkan prediksi, melainkan untuk membuat laporan kepada kita (manusia) seberapa bagus/jelek hasil prediksi model statistik ini (Joke: walau belum tentu akurat, setidaknya komputernya jujur).

Sampailah kita ke hidangan utama dari metode Rational Trend, ratioTrendSeason adalah fungsi yang ketika menerima sebuah set deret angka maka akan mengembalikan 1 deret angka yang isinya adalah rasio yang menurut statistik adalah nilai rasio yang paling mewakili data real yang telah kita berikan. Cara membacanya juga sama seperti data rasio yang telah kita pelajari di Cycle Trend, yaitu, setiap angka yang keluar tersebut melambangkan berapa persen selisih data pada kuartal tersebut terhadap titik tengah tahun tersebut. Jadi bila kita mendapati output [83.20, 99.78, 90.24, 126.77] itu artinya pada kuartal I diprediksi penjualan 83%, kuartal II 99%, kuartal III 90%, dan kuartal IV 126%. Sekarang, mari kita coba ubah menjadi prediksi penjualan sarung toko bapak tadi. Kalau diketahui penjualan kuartal I adalah 5 kuintal, maka nilai tengahnya trendnya adalah 5/83.20 = 6.0092 atau 6 kuintal. Dan bila telah diketahui titik tengah ini maka kita bisa meneruskan prediksi penjualan pada ketiga kuartal berikutnya. Pada kuartal II diperkirakan 60.99 = 5.99 kuintal, kuartal III diperkirakan 690.24 = 5.414 kuintal, kuartal IV diperkirakan 6*1.2677 = 7.60 kuintal. Bila kita bandingkan hasil prediksi antara metode cycle trend dan rational trend, maka pada kasus ini kita dapat melihat adanya perbedaan (bisa jadi pada kasus lain relatif mirip).

Kelebihan metode Rational Trend:

  1. Hasil prediksi (diklaim) lebih akurat dibanding metode lain yang relatif lebih sederhana
  2. Sebelum menunjukkan hasil, model ini mengizinkan kita untuk melihat selisih real vs prediksi

Kekurangan metode Rational Trend:

  1. Menggunakan model matematis yang — sedikit — lebih kompleks

Bedah Kode: Rational Trend

Silahkan baca bagian ini bagi yang berminat untuk mempelajari detail kode JSnya:

ratioTrend = arrays => withAs(
// ^ terima data baris dan kolom
middleIndex(arrays.length), index => ({
// ^ buat sebuah deret middleIndex sepanjang baris
a: sum(arrays.map(sum)) / arrays.length,
// ^ koefisien a itu adalah penjumlahan dari setiap baris
// dibagi dengan banyaknya baris
b: sum(index.map((i, j) => i * sum(arrays[j]))) /
// ^ koefisien b itu adalah penjumlahan dari
// i dikali penjumlahan kolom
sum(index.map(pow(2)))
// ^ dibagi dengan penjumlahan index yg dipangkat 2-kan
})
)

ratioTrendPred = arrays => withAs(ratioTrend(arrays), trend =>
// ^ terima data baris dan kolom
// sebut saja persamaannya sebagai trend ^
chunk(middleIndex(arrays.length * arrays[0].length).map(i =>
// ^ buat sebuah deret middleIndex sepanjang
// panjang baris dikali panjang kolom
(trend.a / 4) + (trend.b / 16 * i)
// ^ sesuaikan dengan rumus aslinya
), 4)
// ^ patahkan hasilnya jadi 4 baris
)

ratioTrendDiff = arrays => withAs(
// ^ terima data baris dan kolom
ratioTrendPred(arrays).flat(), pred =>
// ^ carikan hasil prediksi
// lalu ratakan jadi 1 deret ^
// dan sebut saja sebagai pred ^
chunk(arrays.flatMap(
// ^ ubah setiap angka dalam bahan
(i, j) => i * 100 / pred[j]
// ^ sesuai dengan rumus aslinya
), 4)
// ^ patahkan jadi 4 baris
)

ratioTrendSeason = arrays => withAs(ratioTrendDiff(arrays), diff =>
// ^ terima data basis dan kolom
// carikan tabel selisih nilai real dan prediksi ^
// lalu singkatkan sebagai 'diff' ^
withAs(makeArray(4).map(i => mean(diff.map(j => j[i]))), averages =>
// ^ buat deret sepanjang 4
// cari rata-rata dari setiap baris ^
averages.map(i => i * 100 * 4 / sum(averages))
// ubah setiap angka mean itu sesuai rumus matematisnya
)
)

--

--

No responses yet