Open In Colab

Prediksi Debit Aliran Menggunakan Long Short-Term Memory (LSTM)

Jupyter Notebook (selanjutnya disebut buku) ini hanya contoh dan dibuat untuk pembelajaran mengenai Deep Learning/Neural Networks dan mendemonstrasikan penggunaan Python di bidang sumberdaya air. Buku ini masih perlu dievaluasi kembali jika digunakan untuk kepentingan riset/penelitian ataupun proyek.

Buku ini disertai catatan yang berisikan penjelasan lebih lanjut mengenai buku ini (daftar pustaka, penjelasan dataset, dll). Catatan dapat diunduh di bagian unduh buku.

Catatan

  • Buku ini dikembangkan menggunakan Google Colab, sehingga penampilan terbaik dan interaktif dari buku ini diperoleh jika dibuka melalui Google Colab.
  • Anda dapat mendiskusikan mengenai buku ini (atau hal lainnya seperti koreksi, kritik, saran, pertanyaan, dll) melalui buat diskusi di hidrokit/hidrokit atau dapat menghubungi saya melalui email $hi@taruma.info$.
  • Buku ini masih perlu dievaluasi baik dari teori ataupun implementasi. Ini merupakan buku pribadi yang digunakan oleh saya sebagai latihan implementasi Deep Learning menggunakan Python. Referensi materi pembelajaran saya dapat dilihat pada catatan buku.
  • Biasakan untuk selalu memeriksa kode terlebih dahulu sebelum menjalankannya untuk masalah keamanan.

Deskripsi Kasus

Bagian ini menjelaskan gambaran umum mengenai dataset, permasalahan/tujuan, dan strategi penyelesaiannya.

Dataset

Dataset merupakan data hidrologi dan klimatologi harian dari tanggal 1 Januari 1998 sampai 31 Desember 2008 Daerah Aliran Sungai (DAS) Bendung Baru Pamarayan. Dataset terpisah menjadi 3 kategori yaitu: data curah hujan, data klimatologi, dan data debit.

  • Data curah hujan diperoleh dari 8 stasiun yaitu: bojong_manik, gunung_tunggal, pasir_ona, sampang_peundeuy, cimarga, bd_pamarayan, ciminyak_cilaki, gardu_tanjak.
  • Data debit diperoleh dari 1 stasiun yaitu: bd_pamarayan.
  • Data klimatologi diperoleh dari 1 stasiun yaitu: geofisika_serang.

Rincian mengenai dataset bisa dibaca di catatan buku.

Objektif

Tujuan

  • Peneliti ingin mengetahui nilai debit berdasarkan data hidrologi dan klimatologi yang tersedia pada waktu sebelumnya.

Batasan Masalah

  • Arsitektur (sel) Recurrent Neural Networks yang akan digunakan adalah Long Short-Term Memory (LSTM).
  • Data yang hilang (NaN) diisi menggunakan metode interpolasi linear.
  • Diasumsikan bahwa data tidak perlu diverifikasi.
  • Jika data yang hilang lebih dari 1 tahun berurutan, maka paramater tersebut akan diabaikan.
  • Pelatihan model (training) menggunakan data dari tahun 1998 - 2006.
  • Tidak dilakukan feature engineering, kolom yang bertipe ordinal atau kategori diabaikan.
  • Tidak ada tahapan pemilihan model terbaik (model selection). Parameter akan sembarang mengikuti tulisan Kratzert et al (2018).
  • Berdasarkan Kratzert et al (2018), pada buku ini mengikuti bahwa dataset hanya dibagi dua bagian yaitu train set dan test set, dimana validasi menggunakan test set.

Pertanyaan

  • Berapa nilai debit pada waktu $t$ jika telah diketahui nilai observasi pada waktu $timesteps$ hari sebelumnya?

TAHAP 0: Pengaturan Awal dan Inisiasi

Pada tahap ini akan dilakukan pengaturan awal dan inisiasi dengan melakukan atau menjawab daftar berikut:

  • Menentukan penggunaan runtime lokal atau Google Colab.
  • Menentukan nama buku/proyek dan versi (digunakan jika melakukan penyimpanan).
  • Memeriksa paket hidrokit.
  • Menampilkan versi paket yang digunakan pada sistem.
  • Impor paket utama yang akan digunakan (numpy, pandas, matplotlib).

Pengaturan Buku

In [ ]:
#@title PENGGUNAAN RUNTIME (GOOGLE COLAB/LOKAL) { display-mode: "form" }
#@markdown Centang jika menggunakan sistem lokal.

_IS_LOCALE = False #@param {type:"boolean"}
_server = ['Google Colab', 'LOKAL']
print(':: INFORMASI RUNTIME')
print(':: Buku ini menggunakan RUNTIME: {}'.format(_server[_IS_LOCALE]).upper())
:: INFORMASI RUNTIME
:: BUKU INI MENGGUNAKAN RUNTIME: GOOGLE COLAB
In [ ]:
#@title DESKRIPSI PROYEK { display-mode: "form" }
from datetime import datetime, timezone, timedelta

NOTEBOOK_NAME = 'taruma_demo_lstm_rr' #@param {type:"string"}
NOTEBOOK_VERSION = '1.0.0' #@param {type:"string"}
_current_time_jakarta = datetime.now(timezone(timedelta(hours=7)))
_prefix = _current_time_jakarta.strftime('%Y%m%d_%H%M')
project_title = f'{_prefix}_{NOTEBOOK_NAME}_{NOTEBOOK_VERSION.replace(".", "_")}'

print(':: INFORMASI PROYEK/BUKU')
print(f':: [project_title]: {project_title}')
:: INFORMASI PROYEK/BUKU
:: [project_title]: 20191022_0807_taruma_demo_lstm_rr_1_0_0
In [ ]:
#@title MENGGUNAKAN TENSORFLOW 2.x (Google Colab)

from __future__ import absolute_import, division, print_function, unicode_literals

try:
  # %tensorflow_version only exists in Colab.
  print(':: MENGGUNAKAN TENSORFLOW 2.x (GOOGLE COLAB)')
  %tensorflow_version 2.x
except Exception:
  pass
:: MENGGUNAKAN TENSORFLOW 2.x (GOOGLE COLAB)
TensorFlow 2.x selected.
In [ ]:
#@title PERIKSA PAKET HIDROKIT { display-mode: "form" }
#@markdown Memeriksa paket hidrokit dan memasangnya jika diperlukan.

print(':: memeriksa paket hidrokit'.upper())
if _IS_LOCALE:
    try:
        import hidrokit
    except ModuleNotFoundError:
        print('Hidrokit tidak ditemukan')
else:
    try:
        import hidrokit
    except ModuleNotFoundError:
        print(':: instalasi paket hidrokit'.upper())
        !pip install git+https://github.com/taruma/hidrokit.git@latest -q
:: MEMERIKSA PAKET HIDROKIT
:: INSTALASI PAKET HIDROKIT
  Building wheel for hidrokit (setup.py) ... done
In [ ]:
#@title MENAMPILKAN VERSI PADA SISTEM
def _check_system():
    from numpy import __version__ as numpy_ver
    from pandas import __version__ as pandas_ver
    from matplotlib import __version__ as matplotlib_ver
    from tensorflow import __version__ as tensorflow_ver
    from tensorflow.keras import __version__ as keras_ver
    from hidrokit.__version__ import __version__ as hidrokit_ver
    from sys import version_info as python_info

    ALL_SYSTEM = 'python numpy pandas matplotlib tensorflow keras hidrokit'
    SYSTEM_INFO = {}
    
    print(':: INFORMASI VERSI SISTEM')
    for library in ALL_SYSTEM.split():
        if library.lower() == 'python':
            python_ver = '{}.{}.{}'.format(*python_info[:3])
        print(':: {:>12s} version: {:<10s}'.format(library, eval(library + '_ver')))
        SYSTEM_INFO[library] = eval(library + '_ver')
    
    return SYSTEM_INFO
_SYSTEM_INFO = _check_system()
:: INFORMASI VERSI SISTEM
::       python version: 3.6.8     
::        numpy version: 1.16.5    
::       pandas version: 0.24.2    
::   matplotlib version: 3.0.3     
::   tensorflow version: 2.0.0     
::        keras version: 2.2.4-tf  
::     hidrokit version: 0.3.2     
In [ ]:
#@title LOKASI PENYIMPANAN DATASET
from pathlib import Path

if _IS_LOCALE:
    LOCAL_PATH = Path.home()
    if LOCAL_PATH.as_posix() != 'C:/Users/tarumainfo':
        raise Exception('BUBARKAN!')

    #@markdown ### Pengaturan sistem lokal
    #@markdown Isi alamat relatif terhadap `Pathlib.Path.home()`.
    _LOCAL_DROP = '_dropbox' #@param {type:"string"}
    _LOCAL_DATASET = '_dataset/_uma_pamarayan'  #@param {type:"string"}
    DROP_PATH = LOCAL_PATH / _LOCAL_DROP
    DATASET_PATH = LOCAL_PATH / _LOCAL_DATASET
else:
    # GOOGLE DRIVE
    from google.colab import drive
    drive.mount('/content/gdrive')

    #@markdown ### Pengaturan sistem lokal
    #@markdown Isi alamat relatif terhadap `/content/gdrive/My Drive/Colab Notebooks`.
    DRIVE_PATH = Path('/content/gdrive/My Drive/Colab Notebooks')
    _DRIVE_DROP = '_dropbox' #@param {type:"string"}
    _DRIVE_DATASET = '_dataset/uma_pamarayan' #@param {type:"string"}
    DROP_PATH = DRIVE_PATH / _DRIVE_DROP
    DATASET_PATH = DRIVE_PATH / _DRIVE_DATASET

#@markdown ---
#@markdown ##### Catatan: Gunakan variabel `DROP_PATH` dan `DATASET_PATH` saat berinteraksi dengan fungsi Input/Output (IO).
In [ ]:
print(':: LOKASI PENYIMPANAN DATASET')
print(f'DATASET_PATH = {DATASET_PATH}')
print(f'DROP_PATH = {DROP_PATH}')
:: LOKASI PENYIMPANAN DATASET
DATASET_PATH = /content/gdrive/My Drive/Colab Notebooks/_dataset/uma_pamarayan
DROP_PATH = /content/gdrive/My Drive/Colab Notebooks/_dropbox

Persiapan sistem

In [ ]:
# IMPORT LIBRARIES
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt

print(':: IMPORT LIBRARY (NUMPY, PANDAS, MATPLOTLIB)')
:: IMPORT LIBRARY (NUMPY, PANDAS, MATPLOTLIB)

TAHAP 1: AKUISISI DATASET

Pada tahap ini, tujuan utamanya adalah membaca seluruh dataset yang dimiliki dan mengimpor dataset tersebut untuk pengolahan prapemrosesan data (data preprocessing).

DATA HUJAN

Pada kasus ini, terdapat 8 stasiun yang akan digunakan sehingga terdapat 8 berkas excel. Setiap berkas memiliki data curah hujan dari tahun 1998 hingga 2008 yang disimpan pada masing-masing sheet untuk setiap tahunnya.

Untuk memperoleh data tersebut dalam bentuk tabel (bukan dalam bentuk pivot) digunakan modul yang tersedia di hidrokit (hanya pada versi 0.3.x). Modul dapat diakses melalui hidrokit.contrib.taruma.hk43 (panduan).

In [ ]:
from hidrokit.contrib.taruma import hk43

print(':: MEMBACA DATA HUJAN DARI [DATASET_PATH]')
hujan_raw, hujan_invalid = hk43.read_folder(
    DATASET_PATH, pattern='hujan_*', fmt='uma.hujan', prefix='hujan_',
    invalid=True
)
:: MEMBACA DATA HUJAN DARI [DATASET_PATH]
Found 8 file(s)
::  1  :	hujan_bojong_manik_1998_2008.xls
::  2  :	hujan_gunung_tunggal_1998_2008.xls
::  3  :	hujan_pasir_ona_1998_2008.xls
::  4  :	hujan_sampang_peundeuy_1998_2008.xls
::  5  :	hujan_cimarga_1998_2008.xls
::  6  :	hujan_bd_pamarayan_1998_2008.xls
::  7  :	hujan_ciminyak_cilaki_1998_2008.xls
::  8  :	hujan_gardu_tanjak_1998_2008.xls
In [ ]:
print(f':: tipe [hujan_raw] = {type(hujan_raw)}')
print(f':: tipe [hujan_invalid] = {type(hujan_invalid)}')
:: tipe [hujan_raw] = <class 'dict'>
:: tipe [hujan_invalid] = <class 'dict'>

DATA DEBIT

Untuk data debit, dilakukan hal yang serupa dengan data hujan.

In [ ]:
print(':: MEMBACA DATA DEBIT DARI [DATASET_PATH]')
debit_raw, debit_invalid = hk43.read_folder(
    DATASET_PATH, pattern='debit_*', fmt='uma.debit', prefix='debit_',
    invalid=True
)
:: MEMBACA DATA DEBIT DARI [DATASET_PATH]
Found 1 file(s)
::  1  :	debit_bd_pamarayan_1998_2008.xls
In [ ]:
print(f':: tipe [debit_raw] = {type(debit_raw)}')
print(f':: tipe [debit_invalid] = {type(debit_invalid)}')
:: tipe [debit_raw] = <class 'dict'>
:: tipe [debit_invalid] = <class 'dict'>

DATA KLIMATOLOGI

Data klimatologi diperoleh dari situs data online bmkg. Data klimatologi dari bmkg lebih mudah di impor dikarenakan data sudah tersedia dalam bentuk tabel.

Digunakan modul hidrokit.contrib.taruma.hk73 (panduan) agar memudahkan proses impornya.

In [ ]:
from hidrokit.contrib.taruma import hk73

KLIMATOLOGI_PATH = DATASET_PATH / 'klimatologi_serang_1998_2008.xlsx'
print(f':: [KLIMATOLOGI_PATH] = {KLIMATOLOGI_PATH}')

print(':: MEMBACA DATA KLIMATOLOGI DARI [KLIMATOLOGI_PATH]')
df_klimatologi = hk73._read_bmkg(KLIMATOLOGI_PATH)
print(f':: tipe [df_klimatologi] = {type(df_klimatologi)}')
:: [KLIMATOLOGI_PATH] = /content/gdrive/My Drive/Colab Notebooks/_dataset/uma_pamarayan/klimatologi_serang_1998_2008.xlsx
:: MEMBACA DATA KLIMATOLOGI DARI [KLIMATOLOGI_PATH]
:: tipe [df_klimatologi] = <class 'pandas.core.frame.DataFrame'>

TAHAP 2: PRAPEMROSESAN DATA

Pada tahap ini, dataset yang telah diimpor akan diperiksa dan dipersiapkan untuk pengolahan data di tahap selanjutnya. Berikut yang dilakukan pada tahap ini:

  • Memastikan dataset berupa pandas.DataFrame.
  • Memeriksa data yang invalid (salah input/bukan bilangan).
  • Mengoreksi nilai yang invalid.
  • Mengubah tipe data pada dataframe menjadi numerik.
  • Memeriksa data yang hilang (NaN).
  • Mengisi nilai hilang dengan metode interpolasi linear.
  • Menyesuaikan kelengkapan dataset.

DATA HUJAN

In [ ]:
print(':: MENGUBAH [hujan_raw] MENJADI [df_hujan] SEBAGAI DATAFRAME')
df_hujan = pd.DataFrame(hujan_raw, index=pd.date_range('19980101', '20081231'))
print(':: MENAMPILKAN [df_hujan]')
df_hujan.head()
:: MENGUBAH [hujan_raw] MENJADI [df_hujan] SEBAGAI DATAFRAME
:: MENAMPILKAN [df_hujan]
Out[ ]:
hujan_bojong_manik hujan_gunung_tunggal hujan_pasir_ona hujan_sampang_peundeuy hujan_cimarga hujan_bd_pamarayan hujan_ciminyak_cilaki hujan_gardu_tanjak
1998-01-01 - - - NaN - - - -
1998-01-02 - - - NaN - - 7 -
1998-01-03 5 - - - - 64 - 5
1998-01-04 - - - - - - - -
1998-01-05 - - - - - - - -
In [ ]:
print(':: MEMERIKSA NILAI INVALID PADA [df_hujan]')

print(':: MENAMPILKAN NILAI INVALID')
_hujan_invalid = list(hujan_invalid[list(hujan_invalid.keys())[0]].keys())
print(f':: NILAI INVALID BERUPA = {_hujan_invalid}')
:: MEMERIKSA NILAI INVALID PADA [df_hujan]
:: MENAMPILKAN NILAI INVALID
:: NILAI INVALID BERUPA = ['-', 'NaN']

Dari hujan_invalid diketahui bahwa pada data hujan memiliki data invalid berupa isian "-" dan "NaN". Isian "-" pada data curah hujan menandakan bahwa tidak ada hujan atau bernilai 0.. Sedangkan data "NaN" menandakan bahwa data tidak terekam sama sekali sehingga tidak diketahui terjadi hujan atau tidak.

In [ ]:
print(':: MENGOREKSI NILAI INVALID PADA [df_hujan]')
print(':: MENGOREKSI NILAI "-" MENJADI 0.0')
df_hujan[df_hujan == '-'] = 0.
:: MENGOREKSI NILAI INVALID PADA [df_hujan]
:: MENGOREKSI NILAI "-" MENJADI 0.0
In [ ]:
print(":: MENGUBAH TIPE DATA PADA DATAFRAME [df_hujan]")
df_hujan = df_hujan.infer_objects()
print(":: MENAMPILKAN INFORMASI DATAFRAME [df_hujan]:")
df_hujan.info()
:: MENGUBAH TIPE DATA PADA DATAFRAME [df_hujan]
:: MENAMPILKAN INFORMASI DATAFRAME [df_hujan]:
<class 'pandas.core.frame.DataFrame'>
DatetimeIndex: 4018 entries, 1998-01-01 to 2008-12-31
Freq: D
Data columns (total 8 columns):
hujan_bojong_manik        4017 non-null float64
hujan_gunung_tunggal      4018 non-null float64
hujan_pasir_ona           4018 non-null float64
hujan_sampang_peundeuy    4016 non-null float64
hujan_cimarga             4018 non-null float64
hujan_bd_pamarayan        4016 non-null float64
hujan_ciminyak_cilaki     4018 non-null float64
hujan_gardu_tanjak        4018 non-null float64
dtypes: float64(8)
memory usage: 282.5 KB

Dari informasi diatas diketahui bahwa tipe data pada dataframe telah diubah menjadi berbentuk numerik.

In [ ]:
print(':: MENGISI NILAI HILANG MENGGUNAKAN METODE INTERPOLASI LINEAR')
df_hujan.interpolate(method='linear', inplace=True)
df_hujan.bfill(inplace=True) # Mengatasi nilai hilang pada index [0, 1]
:: MENGISI NILAI HILANG MENGGUNAKAN METODE INTERPOLASI LINEAR
In [ ]:
print(':: MEMERIKSA JIKA [df_hujan] MASIH MEMILIKI NILAI YANG HILANG:', end=' ')
print(f'{df_hujan.isnull().values.any()}')
:: MEMERIKSA JIKA [df_hujan] MASIH MEMILIKI NILAI YANG HILANG: False

DATA DEBIT

Langkahnya serupa dengan data hujan.

In [ ]:
print(':: MENGUBAH [debit_raw] MENJADI [df_debit] SEBAGAI DATAFRAME')
df_debit = pd.DataFrame(debit_raw, index=pd.date_range('19980101', '20081231'))
print(':: MENAMPILKAN [df_debit]')
df_debit.head()
:: MENGUBAH [debit_raw] MENJADI [df_debit] SEBAGAI DATAFRAME
:: MENAMPILKAN [df_debit]
Out[ ]:
debit_bd_pamarayan
1998-01-01 0
1998-01-02 0
1998-01-03 0
1998-01-04 0
1998-01-05 0
In [ ]:
print(':: MEMERIKSA NILAI INVALID PADA [df_debit]')

print(':: MENAMPILKAN NILAI INVALID')
_debit_invalid = list(debit_invalid[list(debit_invalid.keys())[0]].keys())
print(f':: NILAI INVALID BERUPA = {_debit_invalid}')
:: MEMERIKSA NILAI INVALID PADA [df_debit]
:: MENAMPILKAN NILAI INVALID
:: NILAI INVALID BERUPA = ['20.9.46', 'NaN', 'tad']

Dari debit_invalid diketahui bahwa df_debit memiliki nilai invalid berupa "20.9.46", "NaN", dan "tad". Nilai "tad" diartikan sebagai tidak ada data, sedangkan "NaN" adalah data yang hilang. Untuk nilai "20.9.46", diasumsikan terjadi kekeliruan saat memasukkan nilai, nilai tersebut dikoreksi menjadi 209.46.

In [ ]:
print(':: MENGOREKSI NILAI 20.9.46 MENJADI 209.46')
df_debit[df_debit == '20.9.46'] = 209.46
print(':: MENGOREKSI NILAI tad MENJADI NaN')
df_debit[df_debit == 'tad'] = np.nan
:: MENGOREKSI NILAI 20.9.46 MENJADI 209.46
:: MENGOREKSI NILAI tad MENJADI NaN
In [ ]:
print(":: MENGUBAH TIPE DATA PADA DATAFRAME [df_debit]")
df_debit = df_debit.infer_objects()
print(":: MENAMPILKAN INFORMASI DATAFRAME [df_debit]:")
df_debit.info()
:: MENGUBAH TIPE DATA PADA DATAFRAME [df_debit]
:: MENAMPILKAN INFORMASI DATAFRAME [df_debit]:
<class 'pandas.core.frame.DataFrame'>
DatetimeIndex: 4018 entries, 1998-01-01 to 2008-12-31
Freq: D
Data columns (total 1 columns):
debit_bd_pamarayan    4016 non-null float64
dtypes: float64(1)
memory usage: 62.8 KB

Dari informasi diatas diketahui bahwa tipe data pada dataframe telah diubah menjadi berbentuk numerik.

In [ ]:
print(':: MENGISI NILAI HILANG MENGGUNAKAN METODE INTERPOLASI LINEAR')
df_debit.interpolate(method='linear', inplace=True)
:: MENGISI NILAI HILANG MENGGUNAKAN METODE INTERPOLASI LINEAR
In [ ]:
print(':: MEMERIKSA JIKA [df_debit] MASIH MEMILIKI NILAI YANG HILANG:', end=' ')
print(f'{df_debit.isnull().values.any()}')
:: MEMERIKSA JIKA [df_debit] MASIH MEMILIKI NILAI YANG HILANG: False

Nilai 0. dapat berarti terjadi kekeringan atau pengeringan (pada berkas tercantum "pengeringan" pada periode 13 Oktober 2000-31 Oktober 2000).

In [ ]:
print(':: MEMERIKSA KEKERINGAN PADA DATA DEBIT')

# Menggunakan modul hk73 (lihat pada bagian bmkg untuk lebih jelasnya)
from hidrokit.contrib.taruma import hk73

_debit_zero = hk73._get_index1D((df_debit == 0).values[:, 0])
_debit_zero_date = hk73._group_as_index(
    hk73._group_as_list(_debit_zero), index=df_debit.index,
    date_format='%d %b %Y'
)

print(f':: Kekeringan terjadi pada tanggal: {_debit_zero_date}')
:: MEMERIKSA KEKERINGAN PADA DATA DEBIT
:: Kekeringan terjadi pada tanggal: ['01 Jan 1998-28 Feb 1998', '15 Mar 1999', '29 Oct 1999-31 Oct 1999', '13 Oct 2001-31 Oct 2001', '24 Oct 2003-25 Oct 2003', '15 Jun 2004', '31 Aug 2004', '16 Nov 2004-30 Nov 2004', '08 Oct 2005', '11 Oct 2005-13 Oct 2005', '02 Oct 2006-19 Oct 2006', '16 Oct 2007-18 Oct 2007', '07 Sep 2008', '17 Oct 2008-18 Oct 2008']

Pada awal dataset (1 Januari 1998-28 Februari 1998) selama dua bulan bernilai 0. secara beruntun. Saya mengasumsikan ini bukan terkait kekeringan/pengeringan, melainkan data pengukuran dimulai pada bulan maret tahun 1998. Sehingga, saya simpulkan bahwa dalam pemodelan dataset akan dimulai pada tanggal 1 Maret 1998.

DATA KLIMATOLOGI

Proses pada data klimatologi tidak jauh berbeda dengan proses data hujan/debit. Akan tetapi karena data klimatologi berasalkan dari sumber berbeda (BMKG), maka implementasinya akan berbeda dengan implementasi pada data hujan/debit.

Pada modul hidrokit.contrib.taruma.hk73 (panduan) telah disiapkan beberapa fungsi yang memudahkan untuk memeriksa data klimatologi.

Persiapan

Pada tahap 1, data klimatologi telah berbentuk DataFrame, sehingga dapat langsung dilakukan prapemrosesan data.

In [ ]:
print(':: MENAMPILKAN DATAFRAME [df_klimatologi]:')
df_klimatologi.head()
:: MENAMPILKAN DATAFRAME [df_klimatologi]:
Out[ ]:
Tn Tx Tavg RH_avg RR ss ff_x ddd_x ff_avg ddd_car
Tanggal
1998-01-01 23.0 34.6 27.5 75 0.0 5.8 5 225 2 SW
1998-01-02 23.2 34.2 28.6 69 0.0 7.6 4 270 1 NE
1998-01-03 24.0 34.6 27.7 76 0.0 5.6 7 270 2 W
1998-01-04 23.8 34.4 28.4 70 0.0 8.0 7 225 3 SW
1998-01-05 23.5 33.4 27.7 74 1.0 3.5 6 270 2 W

Sebelum melanjutkan dalam prapemrosesan data pada data klimatologi, terdapat beberapa kolom yang dihilangkan karena batasan masalah buku ini dan mempermudah saat pemodelan. Kolom yang digunakan hanya kolom numerik yang bersifat kontinu.
Berikut kolom yang dihilangkan:

  • kolom ddd_car, kolom ini merupakan kolom kategori yang tidak berupa angka.
  • kolom ff_x, ddd_x, ff_avg, kolom ini (dapat) berupa kolom ordinal.

Kolom tersebut dapat diubah melalui proses feature engineering. Referensi lanjut bisa baca di sini dan di sini.

Selain itu, berdasarkan Megariansyah (2015) kolom RR (curah hujan) tidak dapat digunakan karena stasiun tersebut tidak termasuk pada wilayah Daerah Aliran Sungai (DAS) yang dikaji.

In [ ]:
print(':: MEMBERSIHKAN [df_klimatologi] KE DATAFRAME [df_klimatologi_clean]')

df_klimatologi_clean = df_klimatologi.drop(['ddd_car', 'ff_x', 'ddd_x', 'ff_avg', 'RR'], axis=1)
df_klimatologi_clean.head()
:: MEMBERSIHKAN [df_klimatologi] KE DATAFRAME [df_klimatologi_clean]
Out[ ]:
Tn Tx Tavg RH_avg ss
Tanggal
1998-01-01 23.0 34.6 27.5 75 5.8
1998-01-02 23.2 34.2 28.6 69 7.6
1998-01-03 24.0 34.6 27.7 76 5.6
1998-01-04 23.8 34.4 28.4 70 8.0
1998-01-05 23.5 33.4 27.7 74 3.5

Prapemrosesan

Dilanjutkan dengan tahap prapemrosesan seperti memeriksa data invalid ataupun kehilangan data.

In [ ]:
print(':: MEMERIKSA DATA YANG HILANG PADA [df_klimatologi_clean]')

print(f':: [df_klimatologi_clean] memiliki kehilangan data: {hk73._have_nan(df_klimatologi_clean)}')
print(f':: Kolom yang memiliki data hilang: {hk73._get_nan_columns(df_klimatologi_clean)}')
:: MEMERIKSA DATA YANG HILANG PADA [df_klimatologi_clean]
:: [df_klimatologi_clean] memiliki kehilangan data: True
:: Kolom yang memiliki data hilang: ['ss']

Diketahui bahwa pada kolom ss terdapat kehilangan data "NaN". Cek apakah kehilangan data terjadi secara beruntun.

In [ ]:
print(':: MEMERIKSA KONDISI DATA HILANG PADA [df_klimatologi_clean].ss')

_klimatologi_nan = hk73._get_nan(df_klimatologi_clean)
_missing_ss = _klimatologi_nan['ss']
_missing_ss_as_list = hk73._group_as_list(_missing_ss)
_missing_ss_as_index = hk73._group_as_index(
    _missing_ss_as_list, index=df_klimatologi_clean.index, 
    date_format='%d %b %Y'
)

print(f':: Tanggal terjadinya kehilangan data: {_missing_ss_as_index}')
:: MEMERIKSA KONDISI DATA HILANG PADA [df_klimatologi_clean].ss
:: Tanggal terjadinya kehilangan data: ['30 Apr 2003']

Karena kehilangan data hanya terjadi pada satu hari, maka kolom ss akan digunakan dalam pemodelan.

Berdasarkan situs BMKG, harus diperiksa juga mengenai nilai 8888 dan 9999 yang menandakan bahwa data tidak terukur dan/atau tidak ada data. Data tersebut akan dikoreksi menjadi nilai hilang (NaN) dan akan diisi menggunakan metode interpolasi.

In [ ]:
print(':: MEMERIKSA DATA YANG TIDAK ADA/TEREKAM PADA [df_klimatologi_clean]')
_klimatologi_ungauged = hk73._get_missing(df_klimatologi_clean)

print(_klimatologi_ungauged)
:: MEMERIKSA DATA YANG TIDAK ADA/TEREKAM PADA [df_klimatologi_clean]
{'Tn': array([], dtype=int64), 'Tx': array([], dtype=int64), 'Tavg': array([], dtype=int64), 'RH_avg': array([], dtype=int64), 'ss': array([], dtype=int64)}

Ternyata, pada kolom lain tidak memiliki nilai yang tidak terukur/tidak ada. Sehingga, langkah berikutnya mengisi nilai hilang menggunakan metode interpolasi linear.

In [ ]:
print(':: MENGISI NILAI YANG HILANG MENGGUNAKAN METODE INTERPOLASI LINEAR')

df_klimatologi_clean.interpolate(method='linear', inplace=True)
:: MENGISI NILAI YANG HILANG MENGGUNAKAN METODE INTERPOLASI LINEAR
In [ ]:
print(':: MEMERIKSA JIKA [df_klimatologi_clean] MASIH MEMILIKI NILAI YANG HILANG:', end=' ')
print(f'{df_klimatologi_clean.isnull().values.any()}')
:: MEMERIKSA JIKA [df_klimatologi_clean] MASIH MEMILIKI NILAI YANG HILANG: False

PENGGABUNGAN DATASET

Ketiga data yaitu data hujan, data klimatologi, dan data debit digabungkan dalam satu DataFrame untuk pemodelan. Data gabungan sudah dipastikan tidak memiliki nilai yang invalid atau data yang hilang.

Berdasarkan prapemrosesan data debit, diketahui bahwa dua bulan pertama (Januari-Februari 1998) tidak memiliki data, maka dataset akan menggunakan periode yang dimulai dari 1 Maret 1998.

In [ ]:
print(':: MENENTUKAN PERIODE DATASET')

_DATASET_FROM = '19980301'
_DATASET_TO = '20081231'
_slicedate_dataset = slice(_DATASET_FROM, _DATASET_TO)
print(f':: PERIODE DATASET DARI {_DATASET_FROM} hingga {_DATASET_TO}')

print(':: DataFrame [data_hujan], [data_debit], [data_klimatologi]')
data_hujan = df_hujan[_slicedate_dataset]
data_debit = df_debit[_slicedate_dataset]
data_klimatologi = df_klimatologi_clean[_slicedate_dataset]
:: MENENTUKAN PERIODE DATASET
:: PERIODE DATASET DARI 19980301 hingga 20081231
:: DataFrame [data_hujan], [data_debit], [data_klimatologi]
In [ ]:
print(':: MENGGABUNGKAN SELURUH DATA DALAM SATU DATAFRAME [dataset]')
dataset = pd.concat([data_hujan, data_klimatologi, data_debit], axis=1)
:: MENGGABUNGKAN SELURUH DATA DALAM SATU DATAFRAME [dataset]
In [ ]:
#@title Menamai ulang kolom

print(':: MENAMAI ULANG NAMA KOLOM')
COLUMNS_NAME = 'ch_A ch_B ch_C ch_D ch_E ch_F ch_G ch_H suhu_min suhu_max suhu_rerata lembab_rerata lama_penyinaran debit' #@param {type:"string"}
dataset.columns = COLUMNS_NAME.split()
print(':: INFO [dataset]:')
dataset.info()
:: MENAMAI ULANG NAMA KOLOM
:: INFO [dataset]:
<class 'pandas.core.frame.DataFrame'>
DatetimeIndex: 3959 entries, 1998-03-01 to 2008-12-31
Freq: D
Data columns (total 14 columns):
ch_A               3959 non-null float64
ch_B               3959 non-null float64
ch_C               3959 non-null float64
ch_D               3959 non-null float64
ch_E               3959 non-null float64
ch_F               3959 non-null float64
ch_G               3959 non-null float64
ch_H               3959 non-null float64
suhu_min           3959 non-null float64
suhu_max           3959 non-null float64
suhu_rerata        3959 non-null float64
lembab_rerata      3959 non-null int64
lama_penyinaran    3959 non-null float64
debit              3959 non-null float64
dtypes: float64(13), int64(1)
memory usage: 623.9 KB
In [ ]:
print(':: STATISTIK DESKRIPTIF [dataset]')
dataset.describe().T.drop(['count', '25%', '75%'], axis=1)
:: STATISTIK DESKRIPTIF [dataset]
Out[ ]:
mean std min 50% max
ch_A 6.222417 12.044965 0.0 0.00 180.00
ch_B 6.087017 12.304554 0.0 0.00 99.50
ch_C 6.249937 13.616252 0.0 0.00 135.00
ch_D 6.949987 14.510073 0.0 0.00 140.00
ch_E 5.732609 13.728183 0.0 0.00 133.00
ch_F 5.316241 13.329691 0.0 0.00 163.00
ch_G 7.955418 17.472431 0.0 0.00 275.00
ch_H 8.469437 14.768919 0.0 0.00 148.00
suhu_min 23.139227 1.026656 17.4 23.20 26.00
suhu_max 31.826421 1.376400 25.6 32.00 36.20
suhu_rerata 26.734731 0.844836 23.5 26.80 30.10
lembab_rerata 81.678707 5.295782 59.0 82.00 97.00
lama_penyinaran 4.840162 2.721498 0.0 5.20 8.00
debit 72.137286 105.077023 0.0 24.08 2561.58

Visualisasi

In [ ]:
import seaborn as sns
g = sns.pairplot(dataset)
g.fig.set_size_inches(15,15)
In [ ]:
from hidrokit.viz import graph
graph.subplots(dataset, ncols=1, nrows=14, figsize=(15, 15));