Open In Colab

Berdasarkan isu #151: Uji Outlier

Referensi Isu:

Deksripsi Isu:

  • Mencari nilai outlier pada data

Strategi:

  • Buat tabel lampiran yang digunakan untuk mencari nilai $K_n$.
    • Tabel rel_Kn_n, hubungan $K_n$ dengan jumlah data $N$.
  • Membuat fungsi membaca data kemudian mengeluarkan nilai batas bawah dan atas
  • Periksa juga apakah data memiliki outlier atau tidak. Jika iya, mungkin dikeluarkan bagian mana saja yang memiliki outlier.

PERSIAPAN DAN DATASET

In [1]:
import numpy as np
import pandas as pd
In [2]:
# contoh diambil dari buku
# Limantara, Lily M. (2018): Rekayasa Hidrologi, Edisi Revisi. 
# Penerbit Andi Offset, Yogyakarta. (hal. 90-91)

_hujan = np.array([2818, 2542, 1949, 1842, 1748, 1737, 1605, 1558, 1433, 1264])
_index = np.array([2010, 2013, 2008, 2012, 2011, 2014, 2009, 2007, 2015, 2006])
data = pd.DataFrame(
    data=np.stack([_index, _hujan], axis=1), 
    columns=['Tahun','Hujan']
)

# ubah kolom tahun jadi datetime, dan index
data.Tahun = pd.to_datetime(data.Tahun, format="%Y")
data.set_index('Tahun', inplace=True)
data
Out[2]:
Hujan
Tahun
2010-01-01 2818
2013-01-01 2542
2008-01-01 1949
2012-01-01 1842
2011-01-01 1748
2014-01-01 1737
2009-01-01 1605
2007-01-01 1558
2015-01-01 1433
2006-01-01 1264

TABEL

Nilai tabel mengikuti referensi buku.

  • Tabel t_rel_Kn_n diambil pada buku Te Chow, V. (2010). Applied Hydrology. p404.

Tabel yang digunakan dalam perhitungan dibangkitkan dengan kode dibawah ini:

In [3]:
_N = np.array(
    [10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 
     28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 
     46, 47, 48, 49, 50, 55, 60, 65, 70, 75, 80, 85, 90, 95, 100, 110, 120, 
     130, 140]
)

_Kn = np.array(
    [2.036, 2.088, 2.134, 2.175, 2.213, 2.247, 2.279, 2.309, 2.335, 2.361, 
     2.385, 2.408, 2.429, 2.448, 2.467, 2.486, 2.502, 2.519, 2.534, 2.549, 
     2.563, 2.577, 2.591, 2.604, 2.616, 2.628, 2.639, 2.65, 2.661, 2.671, 
     2.682, 2.692, 2.7, 2.71, 2.719, 2.727, 2.736, 2.744, 2.753, 2.76, 
     2.768, 2.804, 2.837, 2.866, 2.893, 2.917, 2.94, 2.961, 2.981, 3, 
     3.017, 3.049, 3.078, 3.104, 3.129]
)

t_rel_Kn_n = pd.DataFrame(np.stack((_N, _Kn), axis=1), columns=['N', 'Kn'])
t_rel_Kn_n.head()
Out[3]:
N Kn
0 10.0 2.036
1 11.0 2.088
2 12.0 2.134
3 13.0 2.175
4 14.0 2.213

KODE

In [4]:
def find_Kn(n, table=t_rel_Kn_n):
    if n < 10 or n > 140:
        raise ValueError('Jumlah data diluar batas bawah (10) / batas atas (140)')
    else:
        N = table['N'].to_numpy()
        Kn = table['Kn'].to_numpy()
        return np.interp(n, N, Kn)

def calc_boundary(df, col=None, result='value', show_stat=False):
    col = df.columns[0] if col is None else col
    
    x = df[col].to_numpy()
    n = x.size
    xlog = np.log10(x)
    xlogmean = xlog.mean()
    xlogstd = xlog.std(ddof=1)

    Kn = find_Kn(n)

    # higher
    y_h = xlogmean + Kn*xlogstd
    val_h = 10**y_h

    # lower
    y_l = xlogmean - Kn*xlogstd
    val_l = 10**y_l

    if show_stat:
        print(
            f'Statistik:',
            f'N = {n}',
            f'Mean (log) = {xlogmean:.5f}',
            f'Std (log) = {xlogstd:.5f}',
            f'Lower (val) = {val_l:.5f}',
            f'Higher (val) = {val_h:.5f}',
            sep='\n', end='\n\n'
        )

    if result.lower() == 'value':
        return (val_l, val_h)
    elif result.lower() == 'log':
        return (y_l, y_h)

def find_outlier(df, col=None, verbose=False, **kwargs):
    
    low, high = calc_boundary(df, col, **kwargs)
    
    col = df.columns[0] if col is None else col

    masklow = df[col] < low
    maskhigh = df[col] > high
    mask = masklow | maskhigh
    
    if verbose and masklow.sum():
        print(f'Ada outlier dibawah batas bawah sebanyak {masklow.sum()}.')
    if verbose and maskhigh.sum():
        print(f'Ada outlier diatas batas atas sebanyak {maskhigh.sum()}.')

    def check_outlier(x):
        if x < low:
            return "lower"
        elif x > high:
            return "higher"
        else:
            return pd.NA

    if mask.sum() != 0:
        new_df = df.copy()
        new_df['outlier'] = df[col].apply(check_outlier)
        return new_df[[col, 'outlier']]
    else:
        print("Tidak ada Outlier")
        return None

FUNGSI

Fungsi find_Kn(n)

Fungsi ini digunakan untuk mencari nilai $K_n$ untuk perhitungan uji outlier.

  • Argumen fungsi:
    • n: jumlah data $\left(10 \le \mathbb{N} \le 140\right)$. Diluar batasan tersebut akan menghasilkan peringatan ValueError.
In [5]:
find_Kn(n=10)
Out[5]:
2.036
In [6]:
try:
    find_Kn(141)
except ValueError:
    print("Hasil akan error")
Hasil akan error

Fungsi calc_boundary(df, col=None, result='value', show_stat=False)

Fungsi calc_boundary(...) digunakan untuk mencari nilai batas bawah dan batas atas outlier dari data. Keluaran fungsi ini berupa tuple dengan bentuk (low_boundary, high_boundary).

  • Argumen Posisi:
    • df: dataset dalam objek pandas.DataFrame.
  • Argumen Opsional:
    • col=None: nama kolom data yang akan dicek outlier. Jika None maka dipilih kolom pertama dari dataframe.
    • result="value": keluaran berupa nilai batasan aktual dengan skala original. Jika menggunakan log, keluaran berupa nilai batasan dalam skala logaritmik.
    • show_stat=False: jika True akan menampilkan nilai statistik berupa jumlah data, rata-rata, standar deviasi, batasan bawah dan atas dalam skala original.
In [7]:
calc_boundary(data)
Out[7]:
(1092.1254115165966, 2961.0550928195808)
In [8]:
calc_boundary(data, col='Hujan', result='log', show_stat=True)
Statistik:
N = 10
Mean (log) = 3.25486
Std (log) = 0.10638
Lower (val) = 1092.12541
Higher (val) = 2961.05509

Out[8]:
(3.038272512363497, 3.471446487863784)

Fungsi find_outlier(df, col=None, verbose=False, **kwargs)

Fungsi find_outlier(...) digunakan untuk memeriksa apakah data memiliki outlier atau tidak dan memberi keluaran berupa dataframe yang telah ditandai data mana saja yang dikategorikan outlier.

  • Argumen Posisi:
    • df: dataset dalam objek pandas.DataFrame.
  • Argumen Opsional:
    • col=None: nama kolom data yang akan dicek outlier. Jika None maka dipilih kolom pertama dari dataframe.
    • verbose=False: memberi informasi tambahan jika memiliki outlier dan seberapa banyak.
    • **kwargs: keyword arguments dari fungsi .calc_boundary().
In [9]:
find_outlier(data)
Tidak ada Outlier
In [10]:
find_outlier(data, show_stat=True)
Statistik:
N = 10
Mean (log) = 3.25486
Std (log) = 0.10638
Lower (val) = 1092.12541
Higher (val) = 2961.05509

Tidak ada Outlier
In [11]:
# contoh data dengan outlier

data2 = data.copy()
data2.loc['2012'] = 4000
In [12]:
find_outlier(data2, 'Hujan', verbose=True)
Ada outlier diatas batas atas sebanyak 1.
Out[12]:
Hujan outlier
Tahun
2010-01-01 2818 <NA>
2013-01-01 2542 <NA>
2008-01-01 1949 <NA>
2012-01-01 4000 higher
2011-01-01 1748 <NA>
2014-01-01 1737 <NA>
2009-01-01 1605 <NA>
2007-01-01 1558 <NA>
2015-01-01 1433 <NA>
2006-01-01 1264 <NA>
In [13]:
data2.loc['2012'] = 40

find_outlier(data2, show_stat=True)
Statistik:
N = 10
Mean (log) = 3.08854
Std (log) = 0.53301
Lower (val) = 100.77150
Higher (val) = 14918.85009

Out[13]:
Hujan outlier
Tahun
2010-01-01 2818 <NA>
2013-01-01 2542 <NA>
2008-01-01 1949 <NA>
2012-01-01 40 lower
2011-01-01 1748 <NA>
2014-01-01 1737 <NA>
2009-01-01 1605 <NA>
2007-01-01 1558 <NA>
2015-01-01 1433 <NA>
2006-01-01 1264 <NA>

Changelog

- 20220303 - 1.0.0 - Initial

Copyright © 2022 Taruma Sakti Megariansyah

Source code in this notebook is licensed under a MIT License. Data in this notebook is licensed under a Creative Common Attribution 4.0 International.