Forensic Analytics dengan Pandas – The Relative Size Factor Test
The Relative Size Factor Test adalah pengujian nilai relatif dalam satu kelompok/subset. Nilai tertinggi/terendah satu kelompok dibandingkan dengan nilai lain pada kelompok tersebut sehingga metode ini efektif untuk mendeteksi kesalahan.
import pandas as pd pd.options.display.float_format = '{:,.2f}'.format sales = pd.read_csv('https://github.com/aansubarkah/basangdata_pandas/raw/master/AW/Sales.SalesOrderHeader.csv', sep='\t') sales2 = sales[sales['CustomerID'].duplicated(keep=False)][['CustomerID', 'SubTotal']] grouped = sales2.groupby(['CustomerID'])['SubTotal'] sales2['first_highest'] = grouped.transform(lambda x: x.nlargest(1).max()) sales2['second_highest'] = grouped.transform(lambda x: x.nlargest(2).values[-1]) sales2.drop_duplicates(subset='CustomerID', inplace=True) sales2['relative_size'] = sales2['first_highest'] / sales2['second_highest'] sales2.sort_values(by='relative_size', ascending=False).head(20)[['CustomerID', 'first_highest', 'second_highest', 'relative_size']]
The Relative Size Factor Test
Metode ini membandingkan nilai tertinggi/terendah dalam satu subset/kelompok dengan nilai tertinggi kedua. Jika terdapat perbedaan signifikan bisa jadi karena:
- Nilai tertinggi tersebut seharusnya masuk pada subset/kelompok lain
- Nilai itu benar masuk pada subset/kelompok tersebut namun terdapat kesalahan pada saat perekaman, misal salah ketik sehingga menambahkan 3 angka 0.
Dalam Forensic Analytics Pak Nigrini merumuskan The Relative Size Factor (RSF) Test sebagai berikut.
Formula tersebut dapat dimodifikasi, misal membagi nilai terbesar/terkecil dengan nilai rata-rata.
Tujuan
Menggunakan data AdventureWorks kita akan menampilkan nilai transaksi tertinggi pertama dan kedua pada masing-masing kelompok/subset pelanggan.
Urgensi
Berikut beberapa potensi temuan yang didapatkan menggunakan pengujian ini (Forensic Analytics halaman 214).
- Nominal salah diinput, misal seharusnya 3.200 namun diinput menjadi 320.000
- Pemeriksaan pada nilai persediaan menunjukkan RSF bernilai 800 yang kemudian diketahui menyebabkan perhitungan laba pada cabang tersebut terlalu tinggi (overstate).
- Data penjualan pabrik diuji pada kelompok nomor SKU (stock keeping unit) dengan membandingkan nilai terendah penjualan per unit dengan nilai rata-rata. Hasilnya beberapa tenaga pemasaran menjual kepada “teman” dengan harga diskon yang bahkan nyaris nol.
Langkah Kerja
Data yang digunakan adalah data penjualan (sebelum pajak dan ongkos kirim) dari tabel Sales.SalesOrderHeader. Data dipilih hanya yang kemunculan CustomerID lebih dari sekali.
Ditambahkan kolom nilai penjualan tertinggi pertama dan kedua. Kemudian kolom ketiga berupa RSF ditambahkan.
Data dalam format csv dapat didownload di sini, notebook di sini.
Kode
Load library dan data
import pandas as pd pd.options.display.float_format = '{:,.2f}'.format sales = pd.read_csv('https://github.com/aansubarkah/basangdata_pandas/raw/master/AW/Sales.SalesOrderHeader.csv', sep='\t')
Gunakan hanya data yang memiliki duplikat
sales2 = sales[sales['CustomerID'].duplicated(keep=False)][['CustomerID', 'SubTotal']]
Tambah kolom
Pertama buat sebuah variabel yang berisi kelompok CustomerID (identitas pelanggan).
grouped = sales2.groupby(['CustomerID'])['SubTotal']
Kemudian tambahkan kolom berisi nilai tertinggi pertama, tertinggi kedua dan kolom RSF. Fungsi drop_duplicates digunakan untuk menghapus data duplikasi CustomerID sehingga hanya terdapat satu data per CustomerID.
sales2['first_highest'] = grouped.transform(lambda x: x.nlargest(1).max()) sales2['second_highest'] = grouped.transform(lambda x: x.nlargest(2).values[-1]) sales2.drop_duplicates(subset='CustomerID', inplace=True) sales2['relative_size'] = sales2['first_highest'] / sales2['second_highest']
Tampilkan RSF
sales2.sort_values(by='relative_size', ascending=False).head(20)[['CustomerID', 'first_highest', 'second_highest', 'relative_size']]
Selanjutnya
Pemeriksaan lebih lanjut perlu menyasar daftar RSF tertinggi di atas. Peningkatan 125 kali lipat dari nilai USD11,40 menjadi USD1.429,41 tentu akan sangat menarik jika ternyata terdapat kesalahan ketik nominal, atau ternyata angka tersebut harusnya ditujukan bagi pelanggan lain.
Referensi
- https://www.nigrini.com/ForensicAnalytics.htm
- https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.DataFrame.duplicated.html
- https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.DataFrame.nlargest.html
- https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.DataFrame.drop_duplicates.html
- https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.DataFrame.transform.html
- https://stackoverflow.com/questions/36310564/pandas-second-max-value-per-group-in-dataframe
Cover Image by mikezwei from Pixabay