Statistik JS: Chap. 4

Riky Perdana
18 min readDec 19, 2022

--

Photo by Markus Krisetya on Unsplash

4.1. Mengenal DF

Menyajikan data mentah berupa himpunan angka baik untuk orang lain maupun diri sendiri akan menguras beban pikiran yang membaca untuk memahaminya tanpa bantuan alat statistik. Adakah cara bagi kita yang lebih mudah untuk merepresentasikan data mentah tanpa perlu bantuan alat statistik yang kompleks? Alternatif jawabannya adalah distribusi frekuensi. Distribusi frekuensi sendiri ada yang tunggal, dan ada juga yang berkelompok. Keduanya bertujuan untuk menyederhanakan data dalam bentuk frekuensi. Distribusi frekuensi tunggal biasanya lebih sederhana karena setiap nilai unik yang ada pada himpunan akan diteli satu per satu sementara pada distribusi frekuensi berkelompok menggunakan rentang nilai yang disebut interval. Kedepannya demi kesederhanaan buku ini kita hanya akan fokus pada Grouped Frequency Distribution atau Distribusi Frekuensi Berkelompok, dan akan kita singkat saja dengan DF. Mari kita bereksperimen dengan sebuah himpunan data tunggal dan DF-nya.

Tabel. Himpunan data tunggal
Tabel. Tabel DF data tadi. Sumber: Hasan, 2012

Diantara 2 tabel diatas, mana yang lebih menarik perhatian, mudah dibaca, dan langsung mengandung informasi yang berguna? Tentu DF kan? Manakah yang lebih mudah untuk diolah oleh alat statistik atau komputer? Sebenarnya sama saja, hanya menurut penulis lebih baik untuk memprioritaskan himpunan data tunggal daripada DF untuk perihal pengolahan data kedepan daripada berangkat ke olah data dari himpunan data yang telah dikonversi ke DF. Kenapa? karena DF berpotensi membiaskan data. Contoh: [1, 2, 3, 4, 5] !== interval 1–5 frek 5. Didasari keyakinan tersebut penulis tetap melibatkan DF buku ini tapi khusus untuk area bahasan dimana menggunakan DF dianggap lebih baik dari sekedar himpunan data tunggal yang mentah.

4.2. Merancang DF

Berhubung DF adalah tentang mengelompokkan data, maka kita perlu kenal komponen pokok sebuah kelompok, yaitu:

  1. Jumlah kelas: seberapa banyak kelompok yang akan dipakai untuk menyebar-ratakan seluruh data.
  2. Interval kelas: seberapa lebar rentang nilai yang akan diwakili oleh setiap kelompok.
  3. Batas Bawah: angka terkecil yang digunakan untuk membatasi rentang kelompok.
  4. Batas Atas: angka terbesar yang digunakan untuk membatasi rentang suatu kelompok
  5. Frekuensi: seberapa banyak data yang nilainya jatuh dalam kelompok tersebut

Hal pertama yang harus kita kerjakan adalah mencari jangkauan data, yaitu selisih antara nilai tertinggi dan nilai terendah dari himpunan data. Bagaimana cara melakukannya dengan JS?

Rumus: Nilai range suatu deret data tunggal
range = array => Math.max(...array) - Math.min(...array)

range(himpunan) // hasilnya 17

Kita akan menentukan sebeapa banyak kelas yang akan dipakai dengan rumus berikut:

Rumus: Jumlah kelas ideal suatu deret angka

Yang bila dikonversi menjadi kode JS adalah sebagai berikut:

classes = array => Math.round(1 + (
3.3 * Math.log10(array.length)
))

classes(himpunan) // hasilnya 6

Bermodal informasi jangkauan data dan banyak kelas kita bisa mulai menghitung interval dalam setiap kelas dengan rumus berikut:

Rumus: Nilai interval sebuah frekuensi

Kode JS-nya adalah:

interval = array => Math.round(range(array)/classes(array))

Bermodal informasi banyak kelas dan interval sekarang kita bisa membuat tabel DF himpunan tersebut dengan fungsi JS sebagai berikut:

// fungsi penguji apakah suatu nilai berada diantara low dan high
between = (low, middle, high) =>
(low <= middle) && (middle <= high)

// fungsi distribusi frekuensi berkelompok
distFreq = array => withThis({
m: Math.min(...array), i: interval(array)
}, ({m, i}) =>
makeArray(classes(array))
.map(j => withThis({
bot: m + (i * j),
top: m + (i * j) + i - 1,
}, ({bot, top}) => ({
bot, top, fre: array.reduce((acc, inc) =>
between(bot, inc, top) ? acc + 1 : acc
, 0)
})))
)

distFreq(data) // panggil fungsinya
// dapatkan hasilnya
/* [
{"bot": 65, "top": 67, "fre": 3 },
{"bot": 68, "top": 70, "fre": 6 },
{"bot": 71, "top": 73, "fre": 12 },
{"bot": 74, "top": 76, "fre": 13 },
{"bot": 77, "top": 79, "fre": 4 },
{"bot": 80, "top": 82, "fre": 2 }
] *

Bila tadi penulis berargumen bahwa untuk akurasi hasil olah data sebaiknya berangkat dari himpunan data tunggal, mari kita simak pula argumen alternatifnya yang justru mendukung konversi data himpunan ke DF terlebih dahulu sebelum ke olah data, terutama karena sifat simplifikasi DF.

Anggaplah Anda adalah seorang analis data di perusahaan startup digital yang ingin mengetahui tanggapan seluruh user yang telah mengupdate aplikasi smartphone-nya ke versi yang terbaru. Berdasarkan survei kuesioner online berskala 0 s/d 100 dalam tempo sebulan didapati sekitar 300.000.000 (tiga ratus juta) pengguna yang merespon. Untuk mengolah 10.000 data saja kebanyakan komputer akan kewalahan, lalu bagaimana dengan data himpunan angka yang panjangnya 300 juta atau lebih? Kemungkinan komputernya akan menolak sedari awal dengan laporan error exceeds processing limit. Belum lagi dengan masalah penyimpanan dan pengiriman data. Jika 1 anggota himpunan seperti 65, membutuhkan 4 byte storage maka 300 juta data tersebut akan memakan ruang 1,2 Gigabyte storage, hanya untuk 1 himpunan. Tidak adakah cara untuk menyederhanakan himpunan angka ini tanpa/minim mengorbankan kualitas? Jawabannya tentu adalah dengan DF.

Jika rentang datanya adalah 100, maka jumlah kelasnya diperkirakan 30 dan masing-masing kelas mengandung informasi bottom, top, dan frekuensi (30 x 3 = 90). Maka dengan 90/300.000.000 = 0,0000001 terdapat 99,99999% penghematan ukuran data. Walaupun data yang akan Anda kerjakan atau temui belum tentu sebanyak itu, tetap DF dapat menunjukkan kelebihannya dalam reduksi data tanpa perlu mengorbankan presisi seperti argumen sebelumnya. Maka bisa kita rumuskan sendiri bahwa bila:

  1. Jumlah data relatif sedikit dan sangat butuh presisi, pilihannya adalah olah data tunggal atau himpunan biasa.
  2. Jumlah data relatif banyak dan tidak terlalu butuh presisi, pilihannya adalah olah data dari DF.

Penulis atau siapapun tidak dapat klaim metode mana yang lebih baik karena alat statistik yang akan Anda gunakan tergantung situasi data yang Anda tangani.

Bedah Kode: Distribusi Frekuensi

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

range = array => Math.max(array) - Math.min(array)
// nilai tertinggi ^ nilai terendah ^

classes = array => Math.round(1 + (
// fungsi pembulatan ^
3.3 * Math.log10(array.length)
// ^ logaritma basis 10
))

interval = array => Math.round(range(array)/classes(array))
// cari range deret ^ ^ dibagi
// hitung pembagian kelasnya ^
// baru bulatkan ^

between = (low, middle, high) =>
// terima angka terendah, tengah, dan tertinggi
(low <= middle) && (middle <= high)
// ^ apakah terendah memang lebih rendah dari tengah?
// ^ apakah tengah lebih rendah dari tertinggi
// ^ jika keduanya benar, berikan True

distFreq = array => withThis({
// ^ terima sebuah deret angka
m: Math.min(...array), i: interval(array)
// m= nilai terkecil pada deret
// i= interval menurut rumus
}, ({m, i}) =>
// ^ serahkan bahan-bahannya
makeArray(classes(array))
// buat deret baru sepanjang kelas
.map(j => withThis({
// ^ untuk setiap item maka..
bot: m + (i * j),
// ^ nilai terbawah untuk kelas itu
top: m + (i * j) + i - 1,
// ^ nilai teratas untuk kelas itu
}, ({bot, top}) => ({
// ^ serahkan bahan-bahannya
bot, top, fre: array.reduce((acc, inc) =>
// ^ hitung berapa kali angka dalam
// kelas tersebut muncul pada deret
between(bot, inc, top) ? acc + 1 : acc
// ^ jika berada diantara bot dan top,
// maka tambahkan ke fre, kalau tidak, jangan
, 0) // hitungan dimulai dari 0
})))
)

4.3. Distribusi Frekuensi Relatif

Menggunakan DF saja untuk menyederhanakan data saja belum memberikan informasi yang lengkap bagi pembacanya. Kadang kita ingin mengetahui seberapa besar proporsi kemunculan data pada setiap kelas dibanding total panjang datanya. Untuk mendapatkan nilai relatif tersebut juga cukup mudah, yaitu dengan membagi nilai frekuensi pada setiap kelas dengan panjang data. Berikut ini adalah contoh kode JS-nya:

distRelative = (dist, percent) => dist.map(
i => Object.assign(i, {
rel: i.fre / sum(dist.map(get('fre')))
* (percent ? 100 : 1)
})
)

distRelative(distFreq(himpunan)) // dengan hasil desimal
/* // dapatkan hasilnya
[
{"bot": 65, "top": 67, "fre": 3, "rel": 0.075 },
{"bot": 68, "top": 70, "fre": 6, "rel": 0.15 },
{"bot": 71, "top": 73, "fre": 12, "rel": 0.3 },
{"bot": 74, "top": 76, "fre": 13, "rel": 0.325 },
{"bot": 77, "top": 79, "fre": 4, "rel": 0.1 },
{"bot": 80, "top": 82, "fre": 2, "rel": 0.05 }
] */

distRelative(distFreq(himpunan), true) // dengan hasil persentase
/* // dapatkan hasilnya
[
{"bot": 65, "top": 67, "fre": 3, "rel": 7.5 },
{"bot": 68, "top": 70, "fre": 6, "rel": 15 },
{"bot": 71, "top": 73, "fre": 12, "rel": 30 },
{"bot": 74, "top": 76, "fre": 13, "rel": 32.5 },
{"bot": 77, "top": 79, "fre": 4, "rel": 10 },
{"bot": 80, "top": 82, "fre": 2, "rel": 5 }
] *

Apa manfaat yang dapat diperoleh dengan mengetahui nilai relatif kelompok? Kita bisa mengetahui proporsi sebaran datanya baik dalam indeks maupun persentase. Dan angka relatif tersebut dapat kita gunakan untuk membuat diagram lingkaran secara manual maupun librari pembuat diagram otomatis yang akan diajarkan pada bab visualisasi data nanti.

Bedah Kode: Distribusi Relatif

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

distRelative = (dist, percent) => dist.map(
// ^ terima tabel distribusi
// ^ kalau minta dalam bentuk persen
i => Object.assign(i, {
// ^ tambahkan informasi 'rel' pada objek
rel: i.fre / sum(dist.map(get('fre')))
// ^ frekuensi pada kelas itu
// ^ penjumlahan dari semua frekuensi
* (percent ? 100 : 1)
// ^ jika diminta persen, kalikan 100
})
)

4.4. Distribusi Frekuensi Kumulatif

Kumulatif artinya nilai frekuensi suatu kelas adalah penjumlahan dari seluruh frekuensi kelas sebelumnya. Akan digunakan untuk apa frekuensi kumulatif tersebut?

  1. untuk mengetahui proporsi kumulatif sejumlah data,
  2. untuk mengembangkan kurva persilangan Ogive,
  3. untuk membantu menghitung median data berkelompok

Membuat kolom frekuensi kumulatif secara manual tentu cukup mudah, karena tinggal menambahkan angka frekuensi pada setiap baris dengan jumlah semua frekuensi sebelumnya, namun bila kita ingin agar komputer yang mengerjakannya maka kode JS-nya adalah sebagai berikut:

distCumulative = dist => dist.reduce(
(acc, inc) => [...acc, Object.assign(inc, {
cumA: inc.fre + sum([0, ...acc.map(get('fre'))]),
cumD: sub([
sum(dist.map(get('fre'))),
sum([0, ...acc.map(get('fre'))]),
])
})],
[])

distCumulative(distFreq(himpunan)) // panggil fungsinya
/* dapatkan hasil seperti ini
[
{"bot": 65, "top": 67, "fre": 3, "cumA": 3, "cumD": 40 },
{"bot": 68, "top": 70, "fre": 6, "cumA": 9, "cumD": 37 },
{"bot": 71, "top": 73, "fre": 12, "cumA": 21, "cumD": 31 },
{"bot": 74, "top": 76, "fre": 13, "cumA": 34, "cumD": 19 },
{"bot": 77, "top": 79, "fre": 4, "cumA": 38, "cumD": 6 },
{"bot": 80, "top": 82, "fre": 2, "cumA": 40, "cumD": 2 }
] */

Pada hasil akan tampak 2 properti baru pada setiap elemen array yaitu kumulatif positif/ascending (cumA) dan kumulatif negatif/descending (cumD). Bila pembaca cermati, akan terlihat bahwa cumD tidak sama dengan kebalikan dari cumA, baik dari tabel maupun dari grafik garis. Adapun contoh kurva ogif adalah seperti pada gambar berikut:

Gambar: Contoh kurva ogive data normal

Bila sudut antar titik dalam kurva curam atau terjal maka dapat diindikasikan bahwa data terkonsentrasi diantara kedua titik tersebut. Sementara pada sudut antara 2 titik yang landai mengindikasikan bahwa konsentrasi data diantara 2 titik tersebut relatif rendah. Sebuah kurva ogive untuk data yang berdistribusi normal akan terlihat terjal atau curam di area tengah dan relatif landai pada pangkal dan ujung data. Titik pertemuan diantara kedua kurva mengindikasikan secara visual letak median atau titik tengah datanya. Uniknya titik pertemuan kurva ogive positif dan negatif ini tidak selalu berada di tengah kurva, mengindikasikan bahwa lokasi titik persilangan menunjukkan konsentrasi nilai data secara umum pada himpunan. Banyak sekali bukan informasi yang bisa didapat dari kurva persilangan ogive? Silahkan googling tentang kurva Ogive bagi yang berminat.

Bedah Kode: Distribusi Kumulatif

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

distCumulative = dist => dist.reduce(
// ^ terima tabel distribusi
(acc, inc) => [...acc, Object.assign(inc, {
// ^ buat deret baru
// ^ tebarkan nilai saldo/accumulated
// ^ ubah isi objek dengan,
cumA: inc.fre + sum([0, ...acc.map(get('fre'))]),
// cumA= kumulasi ascending/meningkat semua frekuensi sebelum
cumD: sub([
// cumD= kumulasi descending/menurun semua frekuensi setelahnya
sum(dist.map(get('fre'))),
sum([0, ...acc.map(get('fre'))]),
])
})],
[]) // dimulai dari deret kosong

4.5. Central Tendency pada DF

Data himpunan yang sudah terlanjur dikonversi /disimplifikasi /disederhanakan menjadi DF bukan berarti tidak dapat dicari central tendency-nya, hanya formulanya yang sedikit lebih rumit. Berikut ini penulis akan menunjukkan ragam formula matematisnya, bagaimana bentuk kode JS-nya, dan contoh keluarannya.

4.5.1. Mean DF

Berdasarkan ilmu statistik, rumus untuk menghitung nilai mean pada tabel distribusi adalah sebagai berikut:

Rumus: Mean untuk distribusi berkelompok

Yang jika dikonversi menjadi kode JS, maka jadinya seperti ini:

distMean = dist => sum(dist.map(
i => (i.bot + (
(i.top - i.bot) / 2
)) * i.fre
)) / sum(dist.map(get('fre')))

distMean(distFreq(himpunan)) // hasilnya 73.12
mean(data) // dapat 73.07, sebagai perbandingan

Pada contoh diatas kita mendapati nilai mean pada data distribusi contoh adalah 73.12, sementara ketika menghitung data tunggal dengan fungsi mean kita mendapati nilai 73.07. Dapat terlihat disini bahwa selisih hasil perhitungan antara fungsi data tunggal dan fungsi data distribusi tidak terpaut jauh. Pada kasus tertentu nilai selisih ini bisa lebih besar atau lebih kecil tergantung pada normalitas data aslinya atau pada jumlah data. Diasumsikan semakin besar data yang dikonversi menjadi tabel frekuensi ini, maka akan semakin kecil pula selisihnya dengan hasil hitung data tunggalnya.

Bedah Kode: Mean Data Distribusi

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

distMean = dist => sum(dist.map(
// ^ terima tabel distribusi
// ^ penjumlahan dari
i => (i.bot + ((i.top - i.bot) / 2))
// hitung nilai tengah kelas itu
* i.fre
// ^ kalikan dengan frekuensinya
)) / sum(dist.map(get('fre')))
// ^ ambil frekuensi semua kelas
// ^ jumlahkan dan jadikan bahan pembagian

4.5.2. Median DF

Serupa dengan tugas median pada data tunggal, fungsi median pada data distribusi juga adalah untuk mencari nilai dengan posisi paling tengah pada suatu deret data. Masalahnya karena data ini sudah terlanjur dalam bentuk tabel frekuensi, kita tidak bisa menggunakan teknik yang sama dengan algoritma pencarian data tunggal. Untuk itu ada formula statistik yang khusus menangani ini:

Rumus: Median untuk data berkelompok

Yang jika dikonversi menjadi kode JS, maka jadinya seperti ini:

distMedian = dist => withThis(
dist[dist.length - 1].cumA,
length => withThis(
dist.find(i => i.cumA >= length / 2),
medClass => (medClass.bot - 0.5) + (
(length/2 - dist.find(
i => i.top === medClass.bot - 1
).cumA) / medClass.fre
) * (medClass.top - medClass.bot + 1)
)
)

distMedian(distCumulative(distFreq(himpunan))) // hasilnya 73.25
median(data) // dapat 73, sebagai perbandingan

Dari contoh kode diatas dapat kita lihat bahwa memanggil fungsi distMedian pada tabel frekuensi contoh akan menghasilkan angka 73.25, sementara ketika data tunggalnya dipanggil dengan fungsi median akan menghasilkan nilai 73. Disampaikan lagi bahwa selisih kecil ini bisa jadi karena data yang normal dan dalam jumlah yang cukup agar hasil tidak bias. Pada data tunggal sebesar atau sekecil apapun Anda akan selalu memperoleh hasil hitung median dengan bilangan bulat, sementara pada kasus ini fungsi distMedian memberikan kita nilai desimal. Kenapa? Karena pada tabel distribusi frekensi tidak ada 'tengah' yang mutlak, melainkan prediksi dimana nilai tengah tersebut berada dan berapa nilainya.

Bedah Kode: Median Data Distribusi

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

distMedian = dist => withThis(
// ^ terima tabel distribusi
dist[dist.length - 1].cumA,
// ^ panjang data cukup diambil dari 'cumA'
length => withThis(
dist.find(i => i.cumA >= length / 2),
// ^ logika pencarian kelas tengah
medClass => (medClass.bot - 0.5) + (
(length/2 - dist.find(
i => i.top === medClass.bot - 1
).cumA) / medClass.fre
) * (medClass.top - medClass.bot + 1)
// sesuaikan dengan kebutuhan rumus
)
)

4.5.3. Moda DF

Sebagaimana kita tahu tentang moda, ia adalah cara untuk menteli angka yang paling sering muncul pada sebuah deret/himpunan. Konsep yang sama juga berlaku pada fungsi moda DF ini, hanya ada perbedaan logika kode karena pada DF yang tersisa hanya angka frekuensi berapa kali nilai dalam rentang sebuah kelas muncul. Khusus untuk moda DF, tersedia formula yang bisa mencarinya, yaitu:

Rumus: Moda untuk data berkelompok

Yang jika dikonversi menjadi kode JS, maka jadinya seperti ini:

distMode = dist => withThis(
dist.reduce((acc, inc) =>
inc.fre > acc.fre ? inc : acc
), mostFre => withThis({
prev: dist.find(i => i.top === mostFre.bot - 1),
next: dist.find(i => i.bot === mostFre.top + 1)
}, ({prev, next}) => (
(mostFre.bot - 0.5) + (
(mostFre.fre - prev.fre) / (
(mostFre.fre - prev.fre) +
(mostFre.fre - next.fre)
)
) * (mostFre.top - mostFre.bot + 1)
))
)

distMode(distFreq(himpunan)) // hasilnya 73.8
mode(data) // dapat 74, sebagai perbandingan

Terlihat pada contoh pemanggilan fungsi distMode pada tabel DF menghasilkan nilai 73.8, dan nilai ini selisih tipis dari nilai moda DF sebesar 74. Besar kecilnya selisih hitung ini karena datanya relatif normal dan berukuran cukup besar. Pada kasus lain bisa saja diperoleh selisih nilai yang berbeda. Namun dengan ini secara umum ditunjukkan bahwa ketika data tunggal dikonversi menjadi data DF maka kemungkinan kualitas hasil hitung mean, median, dan moda-nya tidak berbeda jauh.

Bedah Kode: Median Data Distribusi

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

distMode = dist => withThis(
// ^ terima tabel distribusi
dist.reduce((acc, inc) =>
inc.fre > acc.fre ? inc : acc
// ^ logika pencari kelas dengan frekuensi terbanyak
), mostFre => withThis({
prev: dist.find(i => i.top === mostFre.bot - 1),
// prev= cari 1 kelas sebelum mostFre
next: dist.find(i => i.bot === mostFre.top + 1)
// next= cari 1 kelas setelah mostFre
}, ({prev, next}) => (
// ^ siapkan bahan-bahannya
(mostFre.bot - 0.5) + (
(mostFre.fre - prev.fre) / (
(mostFre.fre - prev.fre) +
(mostFre.fre - next.fre)
)
) * (mostFre.top - mostFre.bot + 1)
// ubah formulanya jadi kode JS
))
)

Bonus Content!

Random Number Generator

Untuk menguji rumus dan kode yang telah kita buat selama ini kita membutuhkan sebuah atau bahkan beberapa himpunan yang jumlah datanya banyak. Untuk mengarang angka-angka tersebut satu per satu cukup melelahkan bagi orang yang kurang rajin (termasuk penulis sendiri). Lalu kenapa kita tidak minta komputer saja yang mengerjakannya dan bagaimana caranya?

randomize = digits => x => Math.round(
Math.random() * Math.pow(10, digits)
)

randomize(5)() // dapat angka acak 92836

distFreq(makeArray(100).map(randomize(2)))
// dapatkan himpunan hasilnya
// hampir dapat dipastikan datanya akan abnormal
/* [
{"bot": 1, "top": 12, "fre": 10 },
{"bot": 13, "top": 24, "fre": 5 },
{"bot": 25, "top": 36, "fre": 17 },
{"bot": 37, "top": 48, "fre": 12 },
{"bot": 49, "top": 60, "fre": 17 },
{"bot": 61, "top": 72, "fre": 13 },
{"bot": 73, "top": 84, "fre": 15 },
{"bot": 85, "top": 96, "fre": 7 }
] */

Contoh kode diatas adalah untuk mendapatkan himpunan 100 angka 2 digit dan langsung didistribusikan frekuensinya. Hanya, perlu untuk diketahui bahwa himpunan angka yang di-generate oleh kode ini hampir dapat dipastikan tidak berdistribusi normal (namanya juga acak).

Array Maker

makeArray = num => [...Array(num).keys()]
// dapat array baru [0, 1, 2, ...num]

Adalah fungsi yang memudahkan kita untuk membuat sebuah himpunan sepanjang angka yang kita sebutkan yang diisi angka berurut mulai dari 0.

Property Getter

get = prop => obj => obj[prop]

Adalah fungsi yang ketika diberikan objek dan nama properti maka akan mengembalikan nilai properti pada objek yang diberikan.

4.6. Fraktil

Gambar: Contoh pembagian fraktil pada kurva data berdistribusi normal

Selain distribusi frekuensi berkelompok terdapat salah satu cara lain yang lebih sederhana dalam penyederhanaan data, yaitu fraktil. Singkatnya fraktil adalah cara kita untuk mempartisi/memecah data dalam ukuran yang sama besar/kecil dan berupaya mengutip informasi dari potongan tersebut. Pada umumnya pemartisian dilakukan dlaam ukuran kuartil, desil, dan persentil. Ini penjelasan ketiganya:

  • Quartile: adalah pemartisian data berdasarkan empat bagian yang sama.
  • Decile: adalah pemartisian data menjadi 10 bagian yang sama.
  • Percentile: adalah pemartisian data menjadi 100 bagian yang sama.
Gambar: Contoh pembagian fraktil pada deret angka tunggal

Untuk menemukan nilai fraktil dapat digunakan rumus berikut:

fraktil = (i * (n + 1)) / partisi

Dimana:
i: fraktil ke berapa yang ingin dicari
n: panjang data
partisi: pecahan fraktil yang diinginkan

Rumus diatas relatif sederhana dan akan segera menemukan masalah ketika panjang himpunan adalah genap. Untuk mengatasi masalah tersebut terdapat perhitungan tambahan yaitu:

fraktil = Xn + (decimalXn * (X(n+1) + Xn))

Lalu bagaimana mentranslasikan rumus ini ke dalam kode JS?

fractile = (array, parts, nth) => withThis(
(nth * (array.length + 1) / parts),
decimal => withThis(Math.floor(decimal),
even => withThis(sort(array),
sorted => sorted[even - 1] + (
(decimal - even) * (
sorted[even] - sorted[even - 1]
)
)
)
)
)

Contoh penggunaannya:

fractile(4, 1, data) // kuartil pertama adalah 71
fractile(4, 2, data) // kuartil kedua adalah 73
fractile(4, 3, data) // kuartil ketiga adalah 75
fractile(10, 3, himpunan) // desil ketiga adalah 71,3
fractile(100, 82, himpunan) // persentil ke-82 adalah 75,62

Sekarang mari kita terjemahkan hasil output yang dikeluarkan fungsi tadi. Untuk kuartil pertama dihasilkan angka 71, angka 73 di Q2 dan 75 di Q3. Tidak ada Q4, karena seperti yang terlihat di gambar, ‘Q’ itu adalah dinding pemisah, dan data di area ujung tidak perlu dipisah lagi. Sebagai contoh, jika Anda ingin memotong sebuah kue menjadi 4 bagian, Anda cukup memotongnya 3 kali kan? Nah, semua hasil tadi menunjukkan bahwa semua angka yang berada di bawah 71 berarti termasuk Q1, semua angka yang berada di bawah 73 termasuk dalam Q2, dan semua yang berada di bawah 75 termasuk dalam Q3. Berarti Q1 berada dalam area Q2, dan Q1+Q2 berada dalam Q3 dong? Tepat sekali. Lalu apa manfaatnya mengetahui area yang bertindihan ini?

Math.min(...data) // get 65
Math.max(...data) // get 82

Kalau kita perhatikan lagi, angka terkecil dalam himpunan kita itu 65 dan angka terbesarnya adalah 82. Kalau Anda jajarkan semua angka dari kecil hingga kanan, dan gunakan angka hasil output fraktil tadi sebagai pembatas, Anda akan tahu bahwa semua yang tergolong Q1 berada +/- 25% dari mean, Q2 tergolong berada +/- 50% dari mean, dan Q3 berjarak +/- 75% dari mean. Semakin besar jarak antara Q1 dan Q3 maka berarti semakin melebar sebarannya. Semakin kecil jarak Q1 dan Q3 maka semakin sempit sebarannya.

Bagaimana dengan desil(= per 10) dan persentil(=per 100)? Konsepnya tetap sama dengan diatas, hanya jumlah dinding pemisahnya yang semakin banyak. Anda ingin menggunakan kuartil, desil, ataupun persentil, fungsi yang digunakan tetap sama. Tinggal disesuaikan saja dengan kebutuhan penelitian.

Bedah Kode: Fraktil Data Tunggal

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

fractile = (array, parts, nth) => withThis(
// ^ terima deret angkanya
// mau dipecah berapa ^
// mau ambil kuartil mana ^
(nth * (array.length + 1) / parts),
// ^ cari nilai desimalnya
decimal => withThis(Math.floor(decimal),
// buat nilai genapnya ^
even => withThis(sort(array),
// urutkan ulang deretnya ^
sorted => sorted[even - 1] + (
(decimal - even) * (
sorted[even] - sorted[even - 1]
)
// sesuaikan dengan rumusnya
)
)
)
)

4.6.1. Fraktil Distribusi Frekuensi

Bagaimana bila backup dat himpunannya sudah tidak ada dan yang tertinggal hanya distribusi frekuensinya saja? Bagaimana cara menghitung fraktil bermodal distribusi frekuensi? Ini rumusnya:

Rumus: Pembagian fraktil pada data distribusi berkelompok
  • Fi: nilai fraktil yang ingin dicari
  • n: jumlah seluruh frekuensi
  • i: fraktil ke berapa yang akan dicari/ditunjuk
  • P: Pecahan fraktil yang ingin digunakan
  • Sigma(fi)*o: jumlah frekuensi semua kelas sebelum fraktil yang ditunjuk
  • C: panjang interval kelas
  • fPi: frekuensi kelas fraktil yang ditunjuk

Yang jika dikonversi menjadi kode JS, maka jadi seperti ini:

distFractile = (parts, num, dist) => withAs(
distCumulative(dist), distCum => withAs(
last(distCum), tail => withAs(
distCum.find(i => i.cumA >= num / parts * tail.cumA)
, qClass => (qClass.bot - 0.5) + (
(
(num / parts * tail.cumA) -
(qClass.cumA - qClass.fre)
) / qClass.fre
) * (qClass.top - qClass.bot + 1)
)
)
)

distFractile(4, 1, distCumulative(distFreq(data))) // kuartil pertama adalah 70.75
distFractile(4, 2, distCumulative(distFreq(data))) // kuartil kedua adalah 73.25
distFractile(10, 5, distCumulative(distFreq(data))) // desil kelima adalah 73.25
distFractile(100, 50, distCumulative(distFreq(data))) // persentil ke-50 adalah 73.25

Bedah Kode: Fraktil Data Tunggal

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

distFractile = (parts, num, dist) => withAs(
// ^ pecahan berapa
// mau pecahan yg mana ^
// berikan distribusinya ^
distCumulative(dist), distCum => withAs(
// buatkan distribusi kumulatifnya
last(distCum), tail => withAs(
// ^ ambil baris terakhirnya
distCum.find(i => i.cumA >= num / parts * tail.cumA)
// ^ carikan kelas letak kuartil yang diminta
, qClass => (qClass.bot - 0.5) + (
(
(num / parts * tail.cumA) -
(qClass.cumA - qClass.fre)
) / qClass.fre
) * (qClass.top - qClass.bot + 1)
// sesuaikan kode JS dengan kebutuhan rumusnya
)
)
)

4.7. Growth Mean

Gambar: Contoh perbandingan garis linear dan kurva

Terdapat satu jenis arithmatic mean yang tidak langsung masuk dalam kategori Pythagorean mean yaitu growth mean. Rata-rata hitung yang satu ini diperuntukkan khusus untuk mencari nilai perkiraan di masa tertentu berdasarkan informasi seperti tahun sebelumnya dan satuan waktu yang diperkirakan. Buku statistik pada umumnya menuliskan formula rata-rata pertumbuhan sebagai berikut:

Rumus: Mean Growth
  • Pt: nilai akhir pertumbuhan
  • Po: nilai awal pertumbuhan
  • Xbar: rata-rata pertumbuhan setiap waktu
  • t: satuan waktu yang digunakan

Formula diatas cocok untuk digunakan bila yang ingin dicari adalah nilai akhir pertumbuhan. Sementara bila yang ingin dicari adalah angka rata-ratanya maka formula diatas perlu diubah dulu bentuknya dengan menempatkan variabel yang dicari di sebelah kiri persamaan. Jika kita telah memiliki 2 buah formula yang mana satunya adalah untuk menentukan rata-rata pertumbuhan dan satunya lagi adalah untuk memprediksi nilai tren masa depan, kedua formula tersebut dapat kita konversi menjadi kode JS sebagai berikut:

meanGrowth = (pt, po, t) =>
(Math.pow((pt / po), 1 / t) - 1) * 100

predictGrowth = (xbar, po, t) =>
po * Math.pow((1 + (xbar / 100)), t)

Sekarang mari kita uji kebergunaan kedua formula tersebut berdasarkan sebuah contoh kasus: Sensus penduduk Indonesia menunjukkan catatan jumlah penduduk pada tahun 2000 adalah 211 juta jiwa sementara 20 tahun berikutnya di 2020 jumlahnya menjadi 270 juta jiwa. Berbekal informasi yang ada coba cari tahu rata-rata pertmbuhan penduduk berikut prediksi jumlah penduduk Indonesia di tahun 2030.

meanGrowth(270, 211, 20) // dapat 1,24 persen
predictGrowth(1.24, 270, 10) // dapat 305 juta jiwa

Dari fungsi meanGrowth kita mendapati bahwa rata-rata pertumbuhan per unit waktunya (tahun) adalah 1,24%. Bermodal persentase tersebut kita bisa memprediksi jumlah penduduk Indonesia tempo 10 tahun kedepan yaitu sekitar 305 juta.

Nah, yang menjadi pertanyaan adalah seberapa tepatkah angka prediksi tersebut? Tentu saja dengan membandingkannya dengan kenyataan. Menggunakan rata-rata pertumbuhan untuk memprediksi nilai tren akan cukup akurat bila garis pertumbuhan atas objek yang diukur cenderung linier. Formula meanGrowth menggunakan Pt dan Po untuk menarik sebuah garis linier yang bisa digunakan untuk memprediksi nilai masa lalu dan masa depan.

--

--

No responses yet