Repository: hidrokit/hidrokit (language: python)

Issue:
bug: AssertionError: (None, <Day>) pada uji `test__dataframe_table`
### Sistem
* Versi hidrokit: 0.3.7
* Distribusi python: 3.6.13
* Sistem operasi: Windows

### Ringkasan Permasalahan

Pada saat testing, hasil dari uji `test_prep_excel.test__dataframe_table` menghasilkan error `AssertionError: (None, <Day>)`. Berdasarkan `pandas-dev/pandas#35446`, kemungkinan error terjadi saat _parsing_ tanggal.

### Error
```python
./tests/test_prep_excel.py::test__dataframe_table Failed: [undefined]AssertionError: (None, <Day>)
def test__dataframe_table():
        filepath = 'tests/data/excel/2013 DUGAAIR.xls'
        pivot = excel._file_single_pivot(filepath, template='pdderi')
        dataframe = excel._dataframe_table(pivot, 2013, name='dugaair')
    
        result = pd.read_csv('tests/data/2013 DUGAAIR.csv',
                             index_col=0, parse_dates=True)
        assert_frame_equal(
>           result, dataframe
        )
E       AssertionError: (None, <Day>)
```

### Solusi

solusi sementara menambah argumen `check_like=True` pada fungsi `assert_frame_equal(...)`

modul rekap untuk uji kecocokan distribusi
### Permintaan fitur/ide

buat modul rekap untuk uji kecocokan distribusi seperti modul `.contrib.taruma.anfrek`

tambah pytest-ci
### Ringkasan _Pull Request_

tambah pytest-ci 
uji KS untuk log pearson 3 probabilitas distribusinya salah
### Sistem
* Versi hidrokit: 0.3.7rc0
* Distribusi python: 3.7.x
* Sistem operasi: Windows (Miniconda)

### Ringkasan Permasalahan

Pada uji KS, nilai probabilitas distribusi (`p_d`) berkebalikan dengan distribusi weibull (`p_w`). Contohnya `p_w=0.05` tapi di `p_d=0.96`, sehingga nilai `d=0.91`. 

Seharusnya nilai `p_w=0.05` dengan nilai `p_d=0.04` (1-p_d) sehingga nilai `d=0.01`. 

Solusinya dengan merevisi fungsi `hidrokit.contrib.taruma.[log pearson 3].calc_prob()`. 

buat modul yang merekap fungsi analisis frekuensi
### Permintaan fitur/ide

buat modul yang merekap fungsi analisis frekuensi

### Alasan/Sumber

karena ke empat distribusi akan sering digunakan, akan lebih baik jika tidak mengingat kode isu untuk melakukan import. 

bug: verbose mode `contrib.taruma.hk98.summary_all`
### Permintaan fitur/ide

Menghilangkan tampilan `PROCESSING: ...`

### Alasan/Sumber

Kalau kolomnya banyak, cukup mengganggu.
modul rekap untuk uji kecocokan distribusi
### Permintaan fitur/ide

buat modul rekap untuk uji kecocokan distribusi seperti modul `.contrib.taruma.anfrek`

buat fungsi CDF untuk masing-masing anfrek
### Permintaan fitur/ide

Menghitung besar nilai "CDF" atau besaran probabilitas jika diketahui besarnya _faktor frekuensi_. 

Ini terkait #127 #172 #124 #126 

### Alasan/Sumber

Fungsi ini terkait #140 (uji kolmogorov-smirnov) karena dibutuhkan nilai probabilitas untuk masing-masing distribusi. Untuk normal dan log normal bisa menggunakan fungsi CDF dari scipy. Sedangkan untuk gumbel dan pearson, diberi alternatif menggunakan hitungan scipy atau tabel. 

### Checklist

- [x] Distribusi Normal
- [x] Distribusi Log Normal
- [x] Distribusi Log Pearson 3
- [x] Distribusi Gumbel
request: Uji Smirnov Kolmogorof
### Permintaan fitur/ide

Uji Smirnov Kolmogorof

### Alasan/Sumber

- Limantara, L. (2018): Rekayasa Hidrologi.
Impor data harian dari excel tanpa memperoleh nama sheet
### Permintaan fitur/ide

Pada modul `.contrib.taruma.hk88` terkait membaca data harian menggunakan template excel yang disiapkan, masih diperlukan input berupa nama stasiunnya. Hal ini bisa dipermudah dengan membaca seluruh _sheet_ yang tidak diawali dengan `_`. Hal ini membuat lebih praktis digunakan saja. 

Fitur ini bisa dikembangkan dari modul `.contrib.taruma.hk88` sebagai modul baru atau memperbarui modul `.hk88`. 

### Alasan/Sumber

- #88 

perbarui changelog
Perbarui changelog
request: kalibrasi model NRECA
### Permintaan fitur/ide

Mengkalibrasi parameter dalam model NRECA untuk memperoleh model terbaik berdasarkan metrik yang digunakan
modul rekap untuk uji kecocokan distribusi
### Permintaan fitur/ide

buat modul rekap untuk uji kecocokan distribusi seperti modul `.contrib.taruma.anfrek`



Discussion / hints:








Sudah ada fungsi Kolmogorov-Smirnov di paket ```scipy``` dengan fungsi ```scipy.stats.kstest```. [^1] Hanya saja pengaplikasiannya ke dalam bidang hidrologi belum coba. Sekilas outputnya bakal berbeda dengan yang biasa di buku hidrologi. Tapi, akan dicoba diperiksa kembali alur kerjanya jika tidak memakan waktu. 

[^1]: https://docs.scipy.org/doc/scipy/reference/generated/scipy.stats.kstest.html


manual: https://gist.github.com/taruma/906e1577111208291e0725229c7d0a76


Fix patch:
diff --git a/.zenodo.json b/.zenodo.json
index 47669fd..b13a7b6 100644
--- a/.zenodo.json
+++ b/.zenodo.json
@@ -1,12 +1,18 @@
 {
     "creators": [
         {
-            "name": "Megariansyah, Taruma"
+            "name": "Megariansyah, Taruma",
+            "orcid": "0000-0002-1551-7673"
         }
     ],
     "description": "hidrokit adalah proyek open source paket python yang dapat digunakan untuk membantu proses analisis hidrologi berupa pengolahan data, analisis data, dan visualisasi data.",
+    "keywords": [
+        "analisis",
+        "hidrologi",
+        "python"
+    ],
     "license": {
         "id": "MIT"
     },
-    "title": "hidrokit: analisis hidrologi dengan python"
-}
\ No newline at end of file
+    "title": "hidrokit"
+}
diff --git a/CHANGELOG.md b/CHANGELOG.md
index be4304b..423e6cb 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,8 +1,6 @@
 # Changelog
 Seluruh perubahan penting pada proyek ini akan didokumentasikan pada bilah ini. Dokumentasi ini hanya untuk perubahan pada paket python.
 
-Changelog ini sudah kadaluarsa dan belum diperbarui lagi.
-
 Penulisan diadaptasi dari [Keep a Changelog](https://keepachangelog.com/id-ID/1.0.0/), dan proyek ini menggunakan penomoran [versi Semantik](https://semver.org/lang/id/spec/v2.0.0.html). 
 
 ---
@@ -12,80 +10,164 @@ Penulisan diadaptasi dari [Keep a Changelog](https://keepachangelog.com/id-ID/1.
 ---
 
 ## Telah Rilis
-### 11 Juli 2019 - v0.2.0
-Ini merupakan perubahan sangat signifikan dari versi sebelumnya dan dimulainya penggunaan layanan integrasi dan _code review_. Perubahan nama fungsi dan modul sangat signifikan sehingga fungsi/metode yang digunakan sebelumnya tidak berfungsi sama sekali (***no backward-compatibility***). Rilis ini disertakan dengan dirilisnya website untuk [hidrokit] dan [hidrokit-nb]. 
 
-**Baru**
+### 2022-04-05 - 0.4.0
+
+**Added/Ditambahkan**
+- Penambahan modul `.contrib.taruma`:
+  - #102 `hk102`: upsampling dataset.
+  - #151 `hk151`: uji outlier.
+  - #158 `hk158`: perhitungan parameter statistik.
+  - Modul Uji Kecocokan Distribusi:
+    - #140 `hk140`: uji kolmogorov-smirnov.
+    - #141 `hk141`: uji chi-square.
+  - Modul Analisis Frekuensi:
+    - #124 `hk124`: distribusi log normal 2 parameter.
+    - #126 `hk126`: distribusi log pearson tipe III.
+    - #127 `hk127`: distribusi gumbel.
+    - #172 `hk172`: distribusi normal.
+  - #179 Fungsi CDF `.calc_prob()` untuk setiap analisis frekuensi.
+  - #194 `anfrek`: modul kumpulan fungsi analisis frekuensi.
+
+**Fixed/Diperbaiki**
+- #169 Perbaikan fungsi test `test_prep_excel.test__dataframe_table()`.
+- #115 Penambahan argumen `verbose=False` pada modul `.contrib.taruma.hk98`.
+- #162 Pembaruan fungsi `.contrib.taruma.hk88.read_workbook()` untuk membaca seluruh _sheet_ tanpa memasukan nama stasiun/_sheet_. 
+
+**Changed/Diubah**
+- #162 Luaran argumen `as_df=False` pada fungsi `.contrib.taruma.hk88.read_workbook()` berubah dari luaran berupa `list` menjadi `dictionary`. 
+
+### 2020-07-13 - 0.3.6
+
+**Fixed/Diperbaiki**
+- Perbaikan argumen `keep_first=False` pada fungsi `timeseries.timestep_table()`.
+
+### 2020-01-15 - 0.3.5
+
+**Added/Ditambahkan**
+- Penambahan modul `.contrib.taruma`:
+  - `hk87`: perhitungan debit andalan menggunakan kurva durasi debit
+  - `hk88`: ambil dataset hujan harian dari excel
+  - `hk89`: pemodelan NRECA
+  - `hk90`: kalibrasi model (NRECA/FJMOCK)
+  - `hk99`: perhitungan curah hujan dengan metode poligon thiessen
+  - `hk98`: rekap dataset deret waktu
+  - `hk96`: pemodelan F.J. Mock
+  - `hk102`: upsampling dataset
+  - `hk106`: perhitungan evapotranspirasi
+
+### 2019-12-09 - 0.3.4
+
+**Added/Ditambahkan**
+- Penambahan modul `.contrib.taruma`:
+  - `hk84`: meringkas informasi data hujan jam-jaman
+
+### 2019-11-30 - 0.3.3
+
+**Added/Ditambahkan**
+- Penambahan modul `.contrib.taruma`:
+  - `hk79`: mengekstrak informasi data jam-jaman dari excel
+- Penambahan `makefile` untuk memudahkan publikasi
+
+**Changed/Diubah**
+- Memperbaiki file sitasi (.cff)
+- Memperbaiki pemanggilan `hidrokit.__version__`
+- Memperbaiki _lint_, yaitu baris yang terlalu panjang
+- 
+
+### 2019-10-15 - 0.3.2
+
+**Added/Ditambahkan**
+- Penambahan subpaket `.contrib.taruma` dengan modul sebagai berikut:
+  - `.hk43`: ubah pivot table ke dataframe
+  - `.hk53`: buat table/tensor untuk pemodelan deep learning LSTM
+  - `.hk73`: mengolah berkas dari bmkg
+- Penambahan subpaket `.contrib` yang digunakan untuk tempat khusus kontributor tanpa pengecekan CI.
+- Penambahan file .cff untuk sitasi dan pembacaan oleh zenodo.
+
+**Changed/Diubah**
+- Perubahan penulisan _tag_ yang berawal dari "v0.3.2" menjadi "0.3.2". Terkait pembacaan otomatis oleh Zenodo.
+
+---
+### 2019-07-26 - v0.2.1
+
+**Changed/Diubah**
+- Added/Ditambahkan argumen `template=` pada fungsi `prep.timeseries.timestep_table`
+- Changed/Diubah penggunaan f-string menjadi `.format` terkait kompatibilitas dengan python 3.5
+- Changed/Diubah konfigurasi codecov.
+- Changed/Diubah konfigurasi travis-ci. Added/Ditambahkan pemeriksaan dengan python 3.5.  
+
+
+### 2019-07-11 - v0.2.0
+
+**Added/Ditambahkan**
 - Penamaan modul dan fungsi yang lebih mudah dibaca dan diingat. 
 - Paket dibagi menjadi tiga bagian utama yaitu persiapan (`prep`), analisis (`analysis`), dan visualisasi (`viz`). Penamaan modul dan fungsi juga diubah dan disesuaikan dengan standar agar lebih jelas dan mudah diingat.
   - Modul untuk membaca data dan manipulasi data seperti `dlkit`, `datakit`, `prepkit` akan dibawah modul `prep` dan digantikan namanya menjadi `timeseries`, `read`, `excel`.
   - Modul untuk menampilkan data seperti `viewkit` dan fungsi plot dari `dlkit` akan dibawah modul `viz` dan berganti nama menjadi `table` dan `graph`. 
   - Beberapa nama fungsi juga berubah menjadi lebih jelas seperti `datakit.dict_null_data` menjadi `prep.read.missing_row()`. 
-- Integrasi [travis-ci], [codecov], [codacy].
+- Integrasi travis-ci, codecov, codacy.
 - Testing menggunakan pytest dengan target 90% _coverage_.
 - Rilisnya website [hidrokit] dan [hidrokit-nb] yang telah disertai panduan berkontribusi dan mengenai proyek pada umumnya. 
 
-**Perbaikan**
+**Fixed/Diperbaiki**
 - Dokumentasi [hidrokit.readthedocs.io] diperbaiki dengan membagi menjadi tiga halaman yaitu data preparation, data analysis, dan data visualization.
 
-**Penghilangan**
+**Removed/Dihilangkan**
 - Modul `bmkgkit` dihapuskan dan belum ada penggantinya.
 - Modul berikut ini telah dihapus/digantikan sehingga tidak akan digunakan lagi:
   - `dlkit`, `viewkit`, `bmkgkit`, `prepkit`, `datakit`.
 
-[travis-ci]: https://travis-ci.com
-[codecov]: https://codecov.io/
-[codacy]: https://www.codacy.com/
-
 ---
+### 2019-06-25 - v0.1.3
+
+**Added/Ditambahkan**
+- Situs dokumentasi teknis di [readthedocs](https://hidrokit.rtfd.io).
+- `docstring` untuk setiap metode.
+
+### 2019-06-10 - v0.1.2
+
+**Added/Ditambahkan**
+- modul `.datakit`.
+- fungsi `.dlkit.table_timesteps()`.
+- argumen baru pada fungsi `.dlkit.multi_column_timesteps()` untuk menyesuaikan fungsi `.dlkit.table_timesteps()`.
+
+**Dokumentasi - d0.1.2**
+- Changed/Diubah templat untuk isu dan PR.
+- tambah dokumen _changelog_.
+- memindahkan _notebook_ ke _repo_ [`hidrokit-nb`](https://github.com/taruma/hidrokit-nb).
+
+### 2019-05-14 - v0.1.1
+
+**Added/Ditambahkan**
+- modul `.dlkit`.
+
+### 2019-01-09 - v0.1.0
+
+**Added/Ditambahkan**
+- Mengubah `hidrokit` sebagai paket python.
+- Distribusi `hidrokit` melalui PyPI.
+
+**Dokumentasi**
+- Changed/Diubah berkas README.
+- Templat isu
+- Kode etik menggunakan _Contributor Covenant_ berbahasa Indonesia.
+- Mengacu halaman wiki untuk panduan kontribusi.
+
+### 2019-01-05 - v0.0.0
+
+**Added/Ditambahkan**
+- `hidrokit` sebagai _module_.
+- Dokumentasi proyek (readme, contributing, etc.).
+- _notebook_ penggunaan `hidrokit`.
+
+### 2018-10-20 - n/a
 
-### 25 Juni 2019 - v0.1.3
-Versi ini fokus ke dokumentasi teknis. Dari versi ini, hidrokit memiliki dokumentasi teknis otomatis yang dibuat menggunakan sphinx dan readthedocs. Situsnya adalah [hidrokit.readthedocs.io].
-- **Penambahan**
-  - Situs dokumentasi teknis di [readthedocs](https://hidrokit.rtfd.io).
-  - `docstring` untuk setiap metode.
-
-### 10 Juni 2019 - v0.1.2
-Modul baru `.datakit` digunakan untuk analisis dan penambahan fungsi `.dlkit.table_timesteps()` untuk mebangkitkan tabel _timesteps_ langsung dari `pandas.DataFrame`. Perubahan signifikan untuk dokumentasi non-teknis. Dokumentasi dapat diakses melalui halaman [hidrokit] atau [hidrokit-nb].
-- **Penambahan**
-  - modul `.datakit`.
-  - fungsi `.dlkit.table_timesteps()`.
-  - argumen baru pada fungsi `.dlkit.multi_column_timesteps()` untuk menyesuaikan fungsi `.dlkit.table_timesteps()`.
-- **Dokumentasi - d0.1.2**
-  - perubahan templat untuk isu dan PR.
-  - tambah dokumen _changelog_.
-  - memindahkan _notebook_ ke _repo_ [`hidrokit-nb`](https://github.com/taruma/hidrokit-nb).
-
-### 14 Mei 2019 - v0.1.1
-Ditambahnya modul baru bernama `dlkit` untuk membantu proses _deep learning_ pada permasalahan _time series_. Modul ini digunakan dalam membuat tabel _timesteps_. 
-- **Penambahan**
-  - modul `.dlkit`.
-
-### 9 Januari 2019 - v0.1.0
-Mengikuti standar distribusi paket Python (PyPI). Memperbaiki dokumentasi komunitas (README, Kode etik). Mengalihkan dokumentasi non-teknis ke halaman wiki.
-- **Penambahan**
-  - Mengubah `hidrokit` sebagai paket python.
-  - Distribusi `hidrokit` melalui PyPI.
-- **Dokumentasi**
-  - Perubahan berkas README.
-  - Templat isu
-  - Kode etik menggunakan _Contributor Covenant_ berbahasa Indonesia.
-  - Mengacu halaman wiki untuk panduan kontribusi.
-
-### 5 Januari 2019 - v0.0.0
-Memulai menandai pengembangan awal `hidrokit` yang masih berupa _module_ python. 
-- **Penambahan**
-  - `hidrokit` sebagai _module_.
-  - Dokumentasi proyek (readme, contributing, etc.).
-  - _notebook_ penggunaan `hidrokit`.
-
-### 20 Oktober 2018 - n/a
-Dibuatnya _repository_ `hidrokit`.
-- **Penambahan**
-  - Membuat _repository_ di GitHub.
+**Added/Ditambahkan**
+- Membuat _repository_ di GitHub.
 
 ---
 
-[hidrokit]: https://taruma.github.io/hidrokit
-[hidrokit-nb]: https://taruma.github.io/hidrokit-nb
-[hidrokit.readthedocs.io]: https://hidrokit.rtfd.io
+[hidrokit]: https://hidrokit.github.io/hidrokit
+[hidrokit-nb]: https://hidrokit.github.io/notebook
+[hidrokit.readthedocs.io]: https://hidrokit.rtfd.io
\ No newline at end of file
diff --git a/CHANGELOG_gen.md b/CHANGELOG_gen.md
new file mode 100644
index 0000000..0a285f7
--- /dev/null
+++ b/CHANGELOG_gen.md
@@ -0,0 +1,273 @@
+# Changelog
+
+## [Unreleased](https://github.com/hidrokit/hidrokit/tree/HEAD)
+
+[Full Changelog](https://github.com/hidrokit/hidrokit/compare/0.3.7rc1...HEAD)
+
+**Implemented enhancements:**
+
+- Fix \#192 RD to Github Actions [\#203](https://github.com/hidrokit/hidrokit/pull/203) ([taruma](https://github.com/taruma))
+
+**Merged pull requests:**
+
+- menambahkan github action untuk jekyll [\#208](https://github.com/hidrokit/hidrokit/pull/208) ([taruma](https://github.com/taruma))
+- remove ci and checkers [\#190](https://github.com/hidrokit/hidrokit/pull/190) ([taruma](https://github.com/taruma))
+- Memperbarui github issue template dan .github [\#188](https://github.com/hidrokit/hidrokit/pull/188) ([taruma](https://github.com/taruma))
+
+## [0.3.7rc1](https://github.com/hidrokit/hidrokit/tree/0.3.7rc1) (2022-03-25)
+
+[Full Changelog](https://github.com/hidrokit/hidrokit/compare/0.3.7rc0...0.3.7rc1)
+
+**Closed issues:**
+
+- pembaruan release drafter dari github app ke github actions [\#192](https://github.com/hidrokit/hidrokit/issues/192)
+
+## [0.3.7rc0](https://github.com/hidrokit/hidrokit/tree/0.3.7rc0) (2022-03-21)
+
+[Full Changelog](https://github.com/hidrokit/hidrokit/compare/0.3.6...0.3.7rc0)
+
+**Fixed bugs:**
+
+- bug: timeseries.timestep\_table error saat menggunakan keep\_first [\#120](https://github.com/hidrokit/hidrokit/issues/120)
+- bug: pada booklet 0.3.5, daftar isi keliru [\#116](https://github.com/hidrokit/hidrokit/issues/116)
+
+**Closed issues:**
+
+- hapus seluruh otomasi pengecekan travis-ci, codacy, codecov [\#189](https://github.com/hidrokit/hidrokit/issues/189)
+- ubah pembuatan isu menjadi lebih sederhana [\#186](https://github.com/hidrokit/hidrokit/issues/186)
+- Presentasi/Demo Milestone 1 \(M1\) [\#161](https://github.com/hidrokit/hidrokit/issues/161)
+- request: demo untuk eksplorasi data hujan [\#145](https://github.com/hidrokit/hidrokit/issues/145)
+- gh: alihkan diskusi ke tab Discussions [\#144](https://github.com/hidrokit/hidrokit/issues/144)
+- request: anfrek: pearson [\#125](https://github.com/hidrokit/hidrokit/issues/125)
+- ask: software HSS [\#113](https://github.com/hidrokit/hidrokit/issues/113)
+
+## [0.3.6](https://github.com/hidrokit/hidrokit/tree/0.3.6) (2020-07-12)
+
+[Full Changelog](https://github.com/hidrokit/hidrokit/compare/0.3.5...0.3.6)
+
+**Fixed bugs:**
+
+- Versi 0.3.6 Bugfix [\#122](https://github.com/hidrokit/hidrokit/pull/122) ([taruma](https://github.com/taruma))
+- Bug/hk120 dan bump to 0.3.6 [\#121](https://github.com/hidrokit/hidrokit/pull/121) ([taruma](https://github.com/taruma))
+
+**Closed issues:**
+
+- site: peringatan/pengumuman migrasi [\#119](https://github.com/hidrokit/hidrokit/issues/119)
+- site: pengalihan ke organisasi hidrokit [\#117](https://github.com/hidrokit/hidrokit/issues/117)
+- gh: update readme [\#111](https://github.com/hidrokit/hidrokit/issues/111)
+- request: Perhitungan HSS ITB [\#97](https://github.com/hidrokit/hidrokit/issues/97)
+- gh: buat peringatan/informasi mengenai pengembangan hidrokit [\#81](https://github.com/hidrokit/hidrokit/issues/81)
+- request: baca tabel di berkas pdf [\#78](https://github.com/hidrokit/hidrokit/issues/78)
+- gh: perbarui README [\#68](https://github.com/hidrokit/hidrokit/issues/68)
+- gh: menyederhanakan pembuatan isu [\#67](https://github.com/hidrokit/hidrokit/issues/67)
+- gh: ganti nama cabang latest jadi develop [\#65](https://github.com/hidrokit/hidrokit/issues/65)
+- ask: Menentukan kriteria berdasarkan total nilai bulanan [\#60](https://github.com/hidrokit/hidrokit/issues/60)
+- ask: Integral untuk mencari volume dari Hidrograf Banjir [\#52](https://github.com/hidrokit/hidrokit/issues/52)
+- ask: daftar berbagai metode praktis [\#14](https://github.com/hidrokit/hidrokit/issues/14)
+
+**Merged pull requests:**
+
+- Migrasi hidrokit/hidrokit [\#118](https://github.com/hidrokit/hidrokit/pull/118) ([taruma](https://github.com/taruma))
+
+## [0.3.5](https://github.com/hidrokit/hidrokit/tree/0.3.5) (2020-01-15)
+
+[Full Changelog](https://github.com/hidrokit/hidrokit/compare/0.3.4...0.3.5)
+
+**Fixed bugs:**
+
+- bug: rekap dari tanggal awal bulan \(taruma.hk98\) [\#104](https://github.com/hidrokit/hidrokit/issues/104)
+- bug: taruma.hk88.read\_workbook  [\#95](https://github.com/hidrokit/hidrokit/issues/95)
+- fix \#104 [\#105](https://github.com/hidrokit/hidrokit/pull/105) ([taruma](https://github.com/taruma))
+- add taruma.hk99 thiessen [\#100](https://github.com/hidrokit/hidrokit/pull/100) ([taruma](https://github.com/taruma))
+
+**Closed issues:**
+
+- gh: pindah gh-pages ke repo tersendiri [\#110](https://github.com/hidrokit/hidrokit/issues/110)
+- request: perbaikan data [\#108](https://github.com/hidrokit/hidrokit/issues/108)
+- request: buat ringkasan tabel jam-jaman dengan durasinya [\#84](https://github.com/hidrokit/hidrokit/issues/84)
+- request: kumpulan pdf taruma.contrib [\#109](https://github.com/hidrokit/hidrokit/issues/109)
+- request: menghitung evapotranspirasi [\#106](https://github.com/hidrokit/hidrokit/issues/106)
+- request: upsampling data [\#102](https://github.com/hidrokit/hidrokit/issues/102)
+- request: buat perhitungan curah hujan dengan metode poligon Thiessen [\#99](https://github.com/hidrokit/hidrokit/issues/99)
+- request: buat ringkasan/rekap data deret waktu [\#98](https://github.com/hidrokit/hidrokit/issues/98)
+- request: Pemodelan FJ Mock [\#96](https://github.com/hidrokit/hidrokit/issues/96)
+- request: kalibrasi model NRECA [\#90](https://github.com/hidrokit/hidrokit/issues/90)
+- request: pemodelan NRECA [\#89](https://github.com/hidrokit/hidrokit/issues/89)
+- request: ambil dataset hujan harian [\#88](https://github.com/hidrokit/hidrokit/issues/88)
+- request: perhitungan debit andalan sungai dengan kurva durasi debit [\#87](https://github.com/hidrokit/hidrokit/issues/87)
+
+**Merged pull requests:**
+
+- add taruma.hk106 evapotranspirasi [\#107](https://github.com/hidrokit/hidrokit/pull/107) ([taruma](https://github.com/taruma))
+- add taruma.hk96 pemodelan FJ Mock [\#103](https://github.com/hidrokit/hidrokit/pull/103) ([taruma](https://github.com/taruma))
+- add taruma.hk98 rekap deret waktu [\#101](https://github.com/hidrokit/hidrokit/pull/101) ([taruma](https://github.com/taruma))
+- add taruma.hk90 kalibrasi model [\#94](https://github.com/hidrokit/hidrokit/pull/94) ([taruma](https://github.com/taruma))
+- add taruma.hk89 model NRECA [\#93](https://github.com/hidrokit/hidrokit/pull/93) ([taruma](https://github.com/taruma))
+- add taruma.hk87 debit andal [\#92](https://github.com/hidrokit/hidrokit/pull/92) ([taruma](https://github.com/taruma))
+- add modul taruma.hk88 [\#91](https://github.com/hidrokit/hidrokit/pull/91) ([taruma](https://github.com/taruma))
+- Update 0.3.5 [\#112](https://github.com/hidrokit/hidrokit/pull/112) ([taruma](https://github.com/taruma))
+
+## [0.3.4](https://github.com/hidrokit/hidrokit/tree/0.3.4) (2019-12-09)
+
+[Full Changelog](https://github.com/hidrokit/hidrokit/compare/0.3.3...0.3.4)
+
+**Closed issues:**
+
+- request: ambil dataset hujan jam-jaman dari excel [\#79](https://github.com/hidrokit/hidrokit/issues/79)
+
+**Merged pull requests:**
+
+- Update 0.3.4 [\#86](https://github.com/hidrokit/hidrokit/pull/86) ([taruma](https://github.com/taruma))
+- contrib.taruma.hk84 [\#85](https://github.com/hidrokit/hidrokit/pull/85) ([taruma](https://github.com/taruma))
+
+## [0.3.3](https://github.com/hidrokit/hidrokit/tree/0.3.3) (2019-11-30)
+
+[Full Changelog](https://github.com/hidrokit/hidrokit/compare/0.3.2...0.3.3)
+
+**Closed issues:**
+
+- request: ubah pivot tabel jam-jaman [\#80](https://github.com/hidrokit/hidrokit/issues/80)
+
+**Merged pull requests:**
+
+- Update 0.3.3 [\#83](https://github.com/hidrokit/hidrokit/pull/83) ([taruma](https://github.com/taruma))
+- 0.3.3 [\#82](https://github.com/hidrokit/hidrokit/pull/82) ([taruma](https://github.com/taruma))
+
+## [0.3.2](https://github.com/hidrokit/hidrokit/tree/0.3.2) (2019-10-15)
+
+[Full Changelog](https://github.com/hidrokit/hidrokit/compare/v0.2.1...0.3.2)
+
+**Fixed bugs:**
+
+- Menambah Citation File Format \(CFF\) [\#77](https://github.com/hidrokit/hidrokit/pull/77) ([taruma](https://github.com/taruma))
+
+**Closed issues:**
+
+- request: mengolah berkas dari data bmkg [\#73](https://github.com/hidrokit/hidrokit/issues/73)
+- request: menyediakan module khusus menyimpan kontribusi orang lain [\#70](https://github.com/hidrokit/hidrokit/issues/70)
+- request: hidrokit 0.2.1 di PyPI [\#66](https://github.com/hidrokit/hidrokit/issues/66)
+- gh: fix release/tag [\#61](https://github.com/hidrokit/hidrokit/issues/61)
+- request: buat tabel/tensor untuk pemodelan deep learning LSTM [\#53](https://github.com/hidrokit/hidrokit/issues/53)
+- ask: ubah pivot table ke dataframe [\#43](https://github.com/hidrokit/hidrokit/issues/43)
+
+**Merged pull requests:**
+
+- fix init and add manual [\#76](https://github.com/hidrokit/hidrokit/pull/76) ([taruma](https://github.com/taruma))
+- Update to 0.3.0 [\#75](https://github.com/hidrokit/hidrokit/pull/75) ([taruma](https://github.com/taruma))
+- ADD taruma.hk73 [\#74](https://github.com/hidrokit/hidrokit/pull/74) ([taruma](https://github.com/taruma))
+- Bump to 0.3.0 [\#72](https://github.com/hidrokit/hidrokit/pull/72) ([taruma](https://github.com/taruma))
+- Subpaket contrib [\#71](https://github.com/hidrokit/hidrokit/pull/71) ([taruma](https://github.com/taruma))
+- clean environment setup [\#69](https://github.com/hidrokit/hidrokit/pull/69) ([taruma](https://github.com/taruma))
+
+## [v0.2.1](https://github.com/hidrokit/hidrokit/tree/v0.2.1) (2019-07-26)
+
+[Full Changelog](https://github.com/hidrokit/hidrokit/compare/v0.2.0...v0.2.1)
+
+**Fixed bugs:**
+
+- bug: python 3.5 support [\#44](https://github.com/hidrokit/hidrokit/issues/44)
+- tambah classifier python 3.5 [\#64](https://github.com/hidrokit/hidrokit/pull/64) ([taruma](https://github.com/taruma))
+- Hotfix 0.2.1 python 3.5 support & codecov configuration [\#63](https://github.com/hidrokit/hidrokit/pull/63) ([taruma](https://github.com/taruma))
+
+**Closed issues:**
+
+- gh: pengaturan release-drafter [\#62](https://github.com/hidrokit/hidrokit/issues/62)
+- site: perbarui search database [\#59](https://github.com/hidrokit/hidrokit/issues/59)
+- gh: label tidak otomatis saat pembuatan isu [\#57](https://github.com/hidrokit/hidrokit/issues/57)
+- site: salah tanggal di changelog untuk v0.2.0 [\#56](https://github.com/hidrokit/hidrokit/issues/56)
+
+## [v0.2.0](https://github.com/hidrokit/hidrokit/tree/v0.2.0) (2019-07-11)
+
+[Full Changelog](https://github.com/hidrokit/hidrokit/compare/v0.1.3...v0.2.0)
+
+**Fixed bugs:**
+
+- bug: tambah classifier [\#25](https://github.com/hidrokit/hidrokit/issues/25)
+- bug: penulisan nama fungsi yang lebih baik dan konsisten [\#21](https://github.com/hidrokit/hidrokit/issues/21)
+
+**Closed issues:**
+
+- site: rombak halaman tentang hidrokit [\#55](https://github.com/hidrokit/hidrokit/issues/55)
+- site: ubah tentang hidrokit jadi abstrak hidrokit [\#54](https://github.com/hidrokit/hidrokit/issues/54)
+- site: permalink halaman tentang hidrokit dan changelog [\#41](https://github.com/hidrokit/hidrokit/issues/41)
+- site: isi halaman kontribusi-situs [\#33](https://github.com/hidrokit/hidrokit/issues/33)
+- site: isi halaman kontribusi-python [\#32](https://github.com/hidrokit/hidrokit/issues/32)
+- site: isi halaman hidrologi [\#31](https://github.com/hidrokit/hidrokit/issues/31)
+- site: isi halaman instalasi [\#28](https://github.com/hidrokit/hidrokit/issues/28)
+- site: Mengalihkan halaman wiki ke GH-Pages [\#24](https://github.com/hidrokit/hidrokit/issues/24)
+- request: mengisi nilai outlier dengan median [\#13](https://github.com/hidrokit/hidrokit/issues/13)
+- request: tambah unit testing [\#12](https://github.com/hidrokit/hidrokit/issues/12)
+
+**Merged pull requests:**
+
+- Release 0.2.0 [\#50](https://github.com/hidrokit/hidrokit/pull/50) ([taruma](https://github.com/taruma))
+
+## [v0.1.3](https://github.com/hidrokit/hidrokit/tree/v0.1.3) (2019-06-25)
+
+[Full Changelog](https://github.com/hidrokit/hidrokit/compare/v0.1.2...v0.1.3)
+
+**Fixed bugs:**
+
+- bug: buat .readthedocs.yml [\#35](https://github.com/hidrokit/hidrokit/issues/35)
+
+**Closed issues:**
+
+- gh: koreksi template [\#34](https://github.com/hidrokit/hidrokit/issues/34)
+- gh: isi halaman kontribusi-dokumentasi [\#30](https://github.com/hidrokit/hidrokit/issues/30)
+- gh: koreksi ./berkontribusi [\#29](https://github.com/hidrokit/hidrokit/issues/29)
+- gh: halaman tentang hidrokit [\#27](https://github.com/hidrokit/hidrokit/issues/27)
+- gh: memperbaiki halaman depan [\#23](https://github.com/hidrokit/hidrokit/issues/23)
+- gh: Ubah tema sesuai dengan hidrokit-nb [\#22](https://github.com/hidrokit/hidrokit/issues/22)
+- wiki: module dan notebook [\#16](https://github.com/hidrokit/hidrokit/issues/16)
+- perbarui template github [\#15](https://github.com/hidrokit/hidrokit/issues/15)
+- Release 0.2.0 [\#6](https://github.com/hidrokit/hidrokit/issues/6)
+- Diskusi Hidrologi 0.2.0 [\#4](https://github.com/hidrokit/hidrokit/issues/4)
+- Diskusi Python 0.2.0 [\#3](https://github.com/hidrokit/hidrokit/issues/3)
+- Selamat Datang di Hidrokit \(0.1.0\) [\#2](https://github.com/hidrokit/hidrokit/issues/2)
+- wiki: pengalihan ke dokumentasi teknis dan hidrokit-nb [\#19](https://github.com/hidrokit/hidrokit/issues/19)
+- feature: dokumentasi sphinx/reST [\#18](https://github.com/hidrokit/hidrokit/issues/18)
+- requirement file [\#11](https://github.com/hidrokit/hidrokit/issues/11)
+
+**Merged pull requests:**
+
+- fix \#34 github template [\#38](https://github.com/hidrokit/hidrokit/pull/38) ([taruma](https://github.com/taruma))
+- konfigurasi stale [\#17](https://github.com/hidrokit/hidrokit/pull/17) ([taruma](https://github.com/taruma))
+- Versi 0.1.3 [\#9](https://github.com/hidrokit/hidrokit/pull/9) ([taruma](https://github.com/taruma))
+- WIP Add Sphinx Support [\#20](https://github.com/hidrokit/hidrokit/pull/20) ([taruma](https://github.com/taruma))
+- Documentation Updates 0.1.2 [\#10](https://github.com/hidrokit/hidrokit/pull/10) ([taruma](https://github.com/taruma))
+
+## [v0.1.2](https://github.com/hidrokit/hidrokit/tree/v0.1.2) (2019-06-10)
+
+[Full Changelog](https://github.com/hidrokit/hidrokit/compare/v0.1.1...v0.1.2)
+
+**Merged pull requests:**
+
+- Versi 0.1.2 [\#8](https://github.com/hidrokit/hidrokit/pull/8) ([taruma](https://github.com/taruma))
+
+## [v0.1.1](https://github.com/hidrokit/hidrokit/tree/v0.1.1) (2019-05-14)
+
+[Full Changelog](https://github.com/hidrokit/hidrokit/compare/v0.1.0...v0.1.1)
+
+**Merged pull requests:**
+
+- Add dlkit function [\#7](https://github.com/hidrokit/hidrokit/pull/7) ([taruma](https://github.com/taruma))
+
+## [v0.1.0](https://github.com/hidrokit/hidrokit/tree/v0.1.0) (2019-01-09)
+
+[Full Changelog](https://github.com/hidrokit/hidrokit/compare/v0.0.0...v0.1.0)
+
+**Merged pull requests:**
+
+- Initial Release 0.1.0 [\#5](https://github.com/hidrokit/hidrokit/pull/5) ([taruma](https://github.com/taruma))
+
+## [v0.0.0](https://github.com/hidrokit/hidrokit/tree/v0.0.0) (2019-01-05)
+
+[Full Changelog](https://github.com/hidrokit/hidrokit/compare/897a93230d87e72d25cfc5cd32ebcef6f173648a...v0.0.0)
+
+**Merged pull requests:**
+
+- Create Code of Conduct [\#1](https://github.com/hidrokit/hidrokit/pull/1) ([taruma](https://github.com/taruma))
+
+
+
+\* *This Changelog was automatically generated by [github_changelog_generator](https://github.com/github-changelog-generator/github-changelog-generator)*
diff --git a/CITATION.cff b/CITATION.cff
index 550bb8c..1ee92cb 100644
--- a/CITATION.cff
+++ b/CITATION.cff
@@ -1,14 +1,14 @@
 --- 
+cff-version: 1.2.0
+message: "Jika menggunakan perangkat lunak ini, mohon sitasi menggunakan metadata ini."
 abstract: "hidrokit adalah proyek open source paket python yang dapat digunakan untuk membantu proses analisis hidrologi berupa pengolahan data, analisis data, dan visualisasi data."
 authors: 
-  - 
-    family-names: Megariansyah
-    given-names: Taruma
-cff-version: "1.0.3"
-date-released: 2020-01-15
+  - family-names: "Megariansyah"
+    given-names: "Taruma"
+    orcid: "https://orcid.org/0000-0002-1551-7673"
+title: "hidrokit"
+date-released: 2022-04-05
+version: "0.4.0"
 doi: 10.5281/zenodo.3276220
 license: MIT
-message: "Jika menggunakan perangkat lunak ini, mohon sitasi menggunakan metadata ini."
 repository-code: "https://github.com/hidrokit/hidrokit"
-title: "hidrokit: Analisis Hidrologi dengan Python"
-version: "0.3.6"
diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md
index 922d259..e6c5dbe 100644
--- a/CODE_OF_CONDUCT.md
+++ b/CODE_OF_CONDUCT.md
@@ -32,7 +32,7 @@ Kode Etik ini berlaku baik di dalam proyek maupun di ruang publik ketika seseora
 
 ## Penegakan
 
-Kejadian perilaku kasar, pelecehan, atau tidak dapat diterima yang terjadi dapat dilaporkan kepada tim inti proyek di timhidrokit@gmail.com. Semua keluhan akan ditinjau dan diselidiki dan akan menghasilkan tanggapan yang dianggap perlu dan sesuai dengan keadaan. Tim inti proyek berkewajiban menjaga kerahasiaan pelapor berkenaan dengan pelapor suatu insiden. 
+Kejadian perilaku kasar, pelecehan, atau tidak dapat diterima yang terjadi dapat dilaporkan kepada tim inti proyek di hi@taruma.info. Semua keluhan akan ditinjau dan diselidiki dan akan menghasilkan tanggapan yang dianggap perlu dan sesuai dengan keadaan. Tim inti proyek berkewajiban menjaga kerahasiaan pelapor berkenaan dengan pelapor suatu insiden. 
 
 Rincian lebih lanjut tentang kebijakan penegakan khusus akan dipublikasikan secara terpisah. Pengelola proyek yang tidak mengikuti atau melaksanakan Kode Etik dengan itikad baik mungkin akan menghadapi dampak sementara atau permanen seperti yang ditentukan oleh para anggota lain dari pimpinan proyek.
 
diff --git a/LICENSE.txt b/LICENSE.txt
index 9409b60..097b0c8 100644
--- a/LICENSE.txt
+++ b/LICENSE.txt
@@ -1,7 +1,6 @@
 MIT License
 
-Copyright (c) 2018-2020, Taruma Sakti Megariansyah
-Copyright (c) 2020, Hidrokit contributors.
+Copyright (c) 2018-2022, Taruma Sakti Megariansyah
 
 Permission is hereby granted, free of charge, to any person obtaining a copy
 of this software and associated documentation files (the "Software"), to deal
diff --git a/README.md b/README.md
index 3159f5a..cff2d94 100644
--- a/README.md
+++ b/README.md
@@ -1,11 +1,12 @@
 
 <div align="center">
-<a href="https://hidrokit.github.io/hidrokit"><img src="https://hidrokit.github.io/hidrokit/assets/images/presskit/hidrokit-800x200.jpg" alt="logo hidrokit"></a><br>
+<a href="https://hidrokit.github.io/hidrokit"><img src="https://hidrokit.github.io/hidrokit/assets/images/presskit/hidrokit-800x200-transparent.png" alt="logo hidrokit"></a><br>
+
+[![Sponsored by PT. FIAKO Enjiniring Indonesia](https://img.shields.io/badge/sponsored%20by-PT.%20FIAKO%20Enjiniring%20Indonesia-blue.svg)](http://www.fiako.co.id/)
 
 ![PyPI - Status](https://img.shields.io/pypi/status/hidrokit.svg)
 ![PyPI - Python Version](https://img.shields.io/pypi/pyversions/hidrokit.svg)
 [![GitHub license](https://img.shields.io/github/license/hidrokit/hidrokit.svg)](https://github.com/hidrokit/hidrokit/blob/master/LICENSE)
-![GitHub tag (latest by date)](https://img.shields.io/github/tag-date/hidrokit/hidrokit.svg?label=recent%20version)
 [![DOI](https://zenodo.org/badge/145389179.svg)](https://zenodo.org/badge/latestdoi/145389179)
 
 <a href="https://hidrokit.github.io/hidrokit"><b>Kunjungi situs resmi hidrokit.</b></a>
@@ -13,17 +14,6 @@
 
 `hidrokit` adalah proyek _open source_ paket *python* yang dapat digunakan untuk membantu proses analisis hidrologi dimulai dari pengolahan data, analisis data, dan visualisasi data. [Baca lebih lanjut mengenai hidrokit](https://hidrokit.github.io/hidrokit/tentang-hidrokit).
 
-## PENGUMUMAN
-
-<div align="center">
-
-**Versi 0.3.5 merupakan versi terakhir hidrokit. Pengembangan hidrokit dalam kondisi hiatus dikarenakan tidak memiliki pengelola. Untuk informasi lebih lanjut baca [tulisan ini](https://medium.com/@taruma/hidrokit-pada-tahun-2020-c00cd8860c0e).**
-
-
-**_Repository hidrokit dialihkan ke hidrokit/hidrokit dari taruma/hidrokit di github._**
-
-</div>
-
 ## Release
 
 <table>
@@ -48,37 +38,6 @@
 
 </table>
 
-
-## Status
-
-<table>
-  <tr align="center">
-    <th>Branch</th>
-    <th>master</th>
-    <th>gh-pages</th>
-  </tr>
-  <tr>
-    <td><b>Services</b></td>
-    <td></td>
-    <td></td>
-  </tr>
-  <tr>
-    <td>Travis-ci</td>
-    <td><img alt="master" src="https://img.shields.io/travis/hidrokit/hidrokit/master.svg?label=build&logo=travis"></td>
-    <td><img alt="gh-pages" src="https://img.shields.io/travis/hidrokit/hidrokit/gh-pages.svg?label=build&logo=travis"></td>
-  </tr>
-  <tr>
-    <td>Codecov</td>
-    <td><img alt="master" src="https://img.shields.io/codecov/c/github/hidrokit/hidrokit/master.svg?logo=codecov"></td>
-    <td></td>
-  </tr>
-  <tr>
-    <td>Codacy</td>
-    <td><img alt="master" src="https://img.shields.io/codacy/grade/63d10854d6d14c79b6162cae547dc594/master.svg?logo=codacy"></td>
-    <td></td>
-  </tr>
-</table>
-
 ## Memulai
 
 Untuk memudahkan penggunaan, disarankan menggunakan **Anaconda3** sebagai distribusi *python*. Download **Anaconda3** [disini](https://www.anaconda.com/download/). Baca panduan di halaman [instalasi untuk lebih detail](https://hidrokit.github.io/hidrokit/panduan/instalasi).
@@ -92,7 +51,7 @@ pip install hidrokit
 ```
 *(akses internet diperlukan saat melakukan pemasangan)*
 
-### Versi terbaru (_unstable_/_latest_)
+### Versi terbaru (_latest_)
 
 Untuk versi paling terbaru bisa melakukan pemasangan berdasarkan cabang _latest_.
 ```bash
@@ -100,7 +59,7 @@ pip install git+https://github.com/hidrokit/hidrokit.git@latest
 ```
 
 ## Catatan penting
-- hidrokit hanya mendukung python versi 3.5 ke atas.
+- Sejak versi 0.3.7, hidrokit hanya mendukung python versi 3.6 ke atas.
 - Pemasangan `xlrd` dibutuhkan jika menggunakan module `excel` untuk membaca bilah _Excel_.
 - Versi 0.2.x tidak memiliki *backward-compatibility* dengan versi 0.1.x.
 - Baca halaman [pemasangan untuk lebih detail](https://hidrokit.github.io/hidrokit/panduan/instalasi).
@@ -111,19 +70,14 @@ Untuk memulai penggunaan, gunakan perintah `import`. Contoh:
 
 ```python
 from hidrokit.prep import read
-from hidrokit.prep.read import missing_row
-from hidrokit.viz import graph
-```
-
-Untuk penggunaan baca halaman [penggunaan](https://hidrokit.github.io/hidrokit/panduan/penggunaan).
-
-## Hidrokit Notebook
 
-Kumpulan contoh _notebook_ dapat diakses di halaman [Hidrokit Notebook](https://hidrokit.github.io/notebook/).
+# untuk penggunaan subpaket .contrib
+# from hidrokit.contrib.[nama user] import [modul]
+from hidrokit.contrib.taruma import anfrek
+from hidrokit.contrib.taruma import hk88, hk102
+```
 
-<div align="center">
-<a href="https://hidrokit.github.io/notebook"><img src="https://hidrokit.github.io/notebook/assets/images/hidrokit-nb-800x200.jpg" width="50%"></a>
-</div>
+Untuk penggunaan baca halaman [penggunaan](https://hidrokit.github.io/hidrokit/panduan/penggunaan). Untuk contoh penggunaan paket hidrokit dan manual bisa lihat di [kumpulan notebook situs hidrokit/notebook](https://hidrokit.github.io/notebook/kumpulan-notebook).
 
 ## Untuk Kontributor
 
@@ -132,3 +86,8 @@ Tertarik menjadi kontributor? Baca [**berkontribusi**](https://hidrokit.github.i
 ## Lisensi
 
 Paket hidrokit menggunakan [lisensi MIT](LICENSE.txt). Dokumentasi yang disertai pada proyek ini menggunakan lisensi [Creative Commons Attribution 4.0 International (CC-BY-4.0)](https://creativecommons.org/licenses/by/4.0/deed.id). 
+## Acknowledgement
+
+- Terima kasih untuk [PT. FIAKO Enjiniring Indonesia](http://www.fiako.co.id/) (FIAKO ENGINEERING) yang telah mensponsori proyek ini sejak Februari 2022.
+- Terima kasih untuk **LKO** yang telah mensponsori proyek ini sejak versi 0.2.x hingga 0.3.6.
+- Terima kasih untuk tim hidrokit-syndicate (_Cahya Suryadi_, _Christine Dorty Hadi_, dan _Dicky Muhammad Fadli_) atas dukungannya dalam pengembangan hidrokit. 
diff --git a/environment-dev.yml b/environment-dev.yml
index e3cb565..62d15ba 100644
--- a/environment-dev.yml
+++ b/environment-dev.yml
@@ -19,4 +19,5 @@ dependencies:
   - sphinx # sphinx settings
   - numpydoc
   - pip # pip installation
+  - scipy
   
diff --git a/hidrokit/__version__.py b/hidrokit/__version__.py
index 27e660b..61a7637 100644
--- a/hidrokit/__version__.py
+++ b/hidrokit/__version__.py
@@ -1,4 +1,8 @@
-VERSION = (0, 3, 6)
+"""
+hidrokit version
+"""
+
+VERSION = (0, 4, 0)
 DEV_STATUS = ""
 
 __version__ = '.'.join(map(str, VERSION)) + DEV_STATUS
diff --git a/hidrokit/contrib/taruma/__init__.py b/hidrokit/contrib/taruma/__init__.py
index 722ff75..d7a31a7 100644
--- a/hidrokit/contrib/taruma/__init__.py
+++ b/hidrokit/contrib/taruma/__init__.py
@@ -1,6 +1,45 @@
-__all__ = (
-    ['hk43', 'hk53', 'hk73', 'hk79'] +
-    ['hk84', 'hk87', 'hk88', 'hk89'] +
-    ['hk90', 'hk96', 'hk98', 'hk99'] +
-    ['hk106']
-)
+
+"""
+0.3.2
+hk43 | Pivot Table
+hk53 | Tensor Input RNN/LSTM
+hk73 | BMKG
+
+0.3.3
+hk79 | Excel Jam-Jaman
+
+0.3.4
+hk84 | Ringkasan Jam-Jaman
+
+0.3.5
+hk87 | Perhitungan Debit Andalan Menggunakan Kurva Durasi Debit
+hk88 | Baca Dataset Harian
+hk89 | Model NRECA
+hk90 | Kalibrasi Model
+hk96 | Model FJMock
+hk98 | Rekap Deret Waktu
+hk99 | Poligon Thiessen
+hk106 | Evapotranspirasi
+
+0.3.7
+hk102 | Upsampling (kelupaan)
+hk124 | Anfrek: Log Normal
+hk126 | Anfrek: Log Pearson 3
+hk127 | Anfrek: Gumbel
+hk140 | Uji Kolmogorov-Smirnov
+hk141 | Uji Chi-Square
+hk151 | Uji Outlier
+hk158 | Parameter Statistik
+hk172 | Anfrek: Normal
+
+anfrek | analisis frekuensi (normal, lognormal, logpearson3, gumbel)
+"""
+
+__all__ = ([
+    'hk43', 'hk53', 'hk73', 'hk79', 'hk84',
+    'hk87', 'hk88', 'hk89', 'hk90', 'hk96',
+    'hk98', 'hk99', 'hk102', 'hk106',
+    'hk124', 'hk126', 'hk127', 'hk140', 'hk141',
+    'hk151', 'hk158', 'hk172',
+    'anfrek',
+])
diff --git a/hidrokit/contrib/taruma/anfrek.py b/hidrokit/contrib/taruma/anfrek.py
new file mode 100644
index 0000000..b0fa21b
--- /dev/null
+++ b/hidrokit/contrib/taruma/anfrek.py
@@ -0,0 +1,15 @@
+"""
+Modul rekap analisis frekuensi. Untuk manual lihat modul terpisah.
+"""
+
+from hidrokit.contrib.taruma import hk172, hk124, hk126, hk127
+
+freq_normal = hk172.freq_normal
+freq_lognormal = hk124.freq_lognormal
+freq_logpearson3 = hk126.freq_logpearson3
+freq_gumbel = hk127.freq_gumbel
+
+normal = hk172
+lognormal = hk124
+logpearson3 = hk126
+gumbel = hk127
diff --git a/hidrokit/contrib/taruma/hk102.py b/hidrokit/contrib/taruma/hk102.py
new file mode 100644
index 0000000..377398e
--- /dev/null
+++ b/hidrokit/contrib/taruma/hk102.py
@@ -0,0 +1,23 @@
+"""manual:
+https://gist.github.com/taruma/96c321175ecac3e51350ef4c94f3d7d4"""
+
+import numpy as np
+import pandas as pd
+
+# ref: https://stackoverflow.com/q/29612705/4886384
+def upsampling(df, freq='D', fill_method='ffill', 
+               use_inter=False, inter_method='linear', inter_keys={}, 
+               reindex=False):
+    start = df.index.min() - pd.DateOffset(day=1)
+    end = df.index.max() - pd.DateOffset(day=31)
+    date = pd.date_range(start, end, freq=freq)
+
+    newdf = df.reindex(date)
+
+    if reindex:
+        return newdf
+
+    if use_inter:
+        return newdf.interpolate(method=inter_method, **inter_keys)
+    else:
+        return newdf.fillna(method=fill_method)
\ No newline at end of file
diff --git a/hidrokit/contrib/taruma/hk124.py b/hidrokit/contrib/taruma/hk124.py
new file mode 100644
index 0000000..3cab044
--- /dev/null
+++ b/hidrokit/contrib/taruma/hk124.py
@@ -0,0 +1,102 @@
+"""manual:
+https://gist.github.com/taruma/5d3ab88893e56f895dc3f36ea19c3e60"""
+
+import numpy as np
+import pandas as pd
+from scipy import stats
+
+# Tabel Nilai Variabel Reduksi Gauss
+# Dari buku hidrologi: Aplikasi Metode Statistik untuk Analisa Data. hal.119
+
+# KODE: SW
+
+_DATA_SW = [
+    [1.001, 0.999, -3.050],
+    [1.005, 0.995, -2.580],
+    [1.010, 0.990, -2.330],
+    [1.050, 0.950, -1.640],
+    [1.110, 0.900, -1.280],
+    [1.250, 0.800, -0.840],
+    [1.330, 0.750, -0.670],
+    [1.430, 0.700, -0.520],
+    [1.670, 0.600, -0.250],
+    [2.000, 0.500, 0.000],
+    [2.500, 0.400, 0.250],
+    [3.330, 0.300, 0.520],
+    [4.000, 0.250, 0.670],
+    [5.000, 0.200, 0.840],
+    [10.000, 0.100, 1.280],
+    [20.000, 0.050, 1.640],
+    [50.000, 0.200, 2.050],
+    [100.000, 0.010, 2.330],
+    [200.000, 0.005, 2.580],
+    [500.000, 0.002, 2.880],
+    [1000.000, 0.001, 3.090],
+]
+
+_COL_SW = ['periode_ulang', 'peluang', 'k']
+
+t_normal_sw = pd.DataFrame(
+    data=_DATA_SW, columns=_COL_SW
+)
+
+def _find_k_in_table(return_period, table):
+    x = table.periode_ulang
+    y = table.k
+    return np.interp(return_period, x, y)
+
+def find_K(return_period, source='scipy'):
+    if source.lower() == 'soewarno':
+        return _find_k_in_table(return_period, t_normal_sw)
+    elif source.lower() == 'scipy':
+        return_period = np.array(return_period)
+        return stats.norm.ppf(1 - 1/return_period)
+
+def calc_x_lognormal(x, return_period=[5], source='scipy', show_stat=False):
+    return_period = np.array(return_period)
+    y = np.log10(x)
+    y_mean = np.mean(y)
+    y_std = np.std(y, ddof=1)
+    n = len(y)
+
+    k = find_K(return_period, source=source)
+
+    if show_stat:
+        print(f'y_mean = {y_mean:.5f}')
+        print(f'y_std = {y_std:.5f}')
+        print(f'k = {k}')
+
+    val_y = y_mean + k * y_std
+    val_x = np.power(10, val_y)
+    return val_x
+
+def freq_lognormal(
+    df, col=None,
+    return_period=[2, 5, 10, 20, 25, 50, 100], show_stat=False, source='scipy',
+    col_name='Log Normal', index_name='Kala Ulang'):
+
+    col = df.columns[0] if col is None else col
+
+    x = df[col].copy()
+
+    arr = calc_x_lognormal(
+        x, return_period=return_period, show_stat=show_stat, source=source)
+
+    result = pd.DataFrame(
+        data=arr, index=return_period, columns=[col_name]
+    )
+
+    result.index.name = index_name
+    return result
+
+def _calc_prob_in_table(k, table):
+    x = table.k
+    y = table.peluang
+    return np.interp(k, x, y)
+
+def calc_prob(k, source='scipy'):
+    if source.lower() == 'soewarno':
+        k = np.array(k)
+        return 1 - _calc_prob_in_table(k, t_normal_sw)
+    elif source.lower() == 'scipy':
+        return stats.norm.cdf(k)
diff --git a/hidrokit/contrib/taruma/hk126.py b/hidrokit/contrib/taruma/hk126.py
new file mode 100644
index 0000000..2e31127
--- /dev/null
+++ b/hidrokit/contrib/taruma/hk126.py
@@ -0,0 +1,322 @@
+"""manual:
+https://gist.github.com/taruma/60725ffca91dc6e741daee9a738a978b"""
+
+import numpy as np
+import pandas as pd
+from scipy import stats, interpolate
+
+# tabel dari Soewarno, hal. 219
+# lampiran tabel III-3, Nilai k Distribusi Pearson tipe III
+# dan Log Pearson ti
+
+# kode: SW
+
+_DATA_SW = np.array([
+    [-0.360, 0.420, 1.180, 2.278, 3.152, 4.051, 4.970, 7.250],
+    [-0.360, 0.518, 1.250, 2.262, 3.048, 3.845, 4.652, 6.600],
+    [-0.330, 0.574, 1.284, 2.240, 2.970, 3.705, 4.444, 6.200],
+    [-0.307, 0.609, 1.302, 2.219, 2.912, 3.605, 4.298, 5.910],
+    [-0.282, 0.643, 1.318, 2.193, 2.848, 3.499, 4.147, 5.660],
+    [-0.254, 0.675, 1.329, 2.163, 2.780, 3.388, 3.990, 5.390],
+    [-0.225, 0.705, 1.337, 2.128, 2.706, 3.271, 3.828, 5.110],
+    [-0.195, 0.732, 1.340, 2.087, 2.626, 3.149, 3.661, 4.820],
+    [-0.164, 0.758, 1.340, 2.043, 2.542, 3.022, 3.489, 4.540],
+    [-0.148, 0.769, 1.339, 2.018, 2.498, 2.957, 3.401, 4.395],
+    [-0.132, 0.780, 1.336, 1.998, 2.453, 2.891, 3.312, 4.250],
+    [-0.116, 0.790, 1.333, 1.967, 2.407, 2.824, 3.223, 4.105],
+    [0.099, 0.800, 1.328, 1.939, 2.359, 2.755, 3.132, 3.960],
+    [-0.083, 0.808, 1.323, 1.910, 2.311, 2.686, 3.041, 3.815],
+    [-0.066, 0.816, 1.317, 1.880, 2.261, 2.615, 2.949, 3.670],
+    [-0.050, 0.824, 1.309, 1.849, 2.211, 2.544, 2.856, 3.525],
+    [-0.033, 0.830, 1.301, 1.818, 2.159, 2.472, 2.763, 3.380],
+    [-0.017, 0.836, 1.292, 1.785, 2.107, 2.400, 2.670, 3.235],
+    [0.000, 0.842, 1.282, 1.751, 2.054, 2.326, 2.576, 3.090],
+    [0.017, 0.836, 1.270, 1.761, 2.000, 2.252, 2.482, 3.950],
+    [0.033, 0.850, 1.258, 1.680, 1.945, 2.178, 2.388, 2.810],
+    [0.050, 0.853, 1.245, 1.643, 1.890, 2.104, 2.294, 2.675],
+    [0.066, 0.855, 1.231, 1.606, 1.834, 2.029, 2.201, 2.540],
+    [0.083, 0.856, 1.216, 1.567, 1.777, 1.955, 2.108, 2.400],
+    [0.099, 0.857, 1.200, 1.528, 1.720, 1.880, 2.016, 2.275],
+    [0.116, 0.857, 1.183, 1.488, 1.663, 1.806, 1.926, 2.150],
+    [0.132, 0.856, 1.166, 1.448, 1.606, 1.733, 1.837, 2.035],
+    [0.148, 0.854, 1.147, 1.407, 1.549, 1.660, 1.749, 1.910],
+    [0.164, 0.852, 1.128, 1.366, 1.492, 1.588, 1.664, 1.800],
+    [0.195, 0.844, 1.086, 1.282, 1.379, 1.449, 1.501, 1.625],
+    [0.225, 0.832, 1.041, 1.198, 1.270, 1.318, 1.351, 1.465],
+    [0.254, 0.817, 0.994, 1.116, 1.166, 1.197, 1.216, 1.280],
+    [0.282, 0.799, 0.945, 1.035, 1.069, 1.087, 1.097, 1.130],
+    [0.307, 0.777, 0.895, 0.959, 0.980, 0.990, 1.995, 1.000],
+    [0.330, 0.752, 0.844, 0.888, 0.900, 0.905, 0.907, 0.910],
+    [0.360, 0.711, 0.771, 0.793, 0.798, 0.799, 0.800, 0.802],
+    [0.396, 0.636, 0.660, 0.666, 0.666, 0.667, 0.667, 0.668]]
+)
+
+_INDEX_SW = [
+    3, 2.5, 2.2, 2, 1.8, 1.6, 1.4, 1.2, 1,
+    0.9, 0.8, 0.7, 0.6, 0.5, 0.4, 0.3, 0.2, 0.1, 0,
+    -0.1, -0.2, -0.3, -0.4, -0.5, -0.6, -0.7, -0.8, -0.9, -1,
+    -1.2, -1.4, -1.6, -1.8, -2., -2.2, -2.5, -3.
+]
+
+_COL_SW = [0.5, 0.2, 0.1, 0.04, 0.02, 0.01, 0.005, 0.001]
+
+t_pearson3_sw = pd.DataFrame(data=_DATA_SW, index=_INDEX_SW, columns=_COL_SW)
+
+# Tabel dari Soetopo hal. 105
+# Tabel Distribusi Pearson Type III (nilai K)
+
+# KODE: ST
+
+_DATA_ST = [
+    [-4.051, -2.003, -1.180, -0.420, 0.396, 0.636, 0.660, 0.666, 0.666, 0.667, 0.667, 0.667],
+    [-4.013, -2.007, -1.195, -0.440, 0.390, 0.651, 0.681, 0.688, 0.689, 0.690, 0.690, 0.690],
+    [-3.973, -2.010, -1.210, -0.460, 0.384, 0.666, 0.702, 0.712, 0.714, 0.714, 0.714, 0.714],
+    [-3.932, -2.012, -1.224, -0.479, 0.376, 0.681, 0.724, 0.738, 0.740, 0.740, 0.741, 0.741],
+    [-3.889, -2.013, -1.238, -0.499, 0.369, 0.696, 0.747, 0.765, 0.768, 0.769, 0.769, 0.769],
+    [-3.845, -2.012, -1.250, -0.518, 0.360, 0.711, 0.771, 0.793, 0.798, 0.799, 0.800, 0.800],
+    [-3.800, -2.011, -1.262, -0.537, 0.351, 0.725, 0.795, 0.823, 0.830, 0.832, 0.833, 0.833],
+    [-3.753, -2.009, -1.274, -0.555, 0.341, 0.739, 0.819, 0.855, 0.864, 0.867, 0.869, 0.869],
+    [-3.705, -2.006, -1.284, -0.574, 0.330, 0.752, 0.844, 0.888, 0.900, 0.905, 0.907, 0.909],
+    [-3.656, -2.001, -1.294, -0.592, 0.319, 0.765, 0.869, 0.923, 0.939, 0.946, 0.949, 0.952],
+    [-3.605, -1.996, -1.303, -0.609, 0.307, 0.777, 0.895, 0.959, 0.980, 0.990, 0.995, 0.999],
+    [-3.553, -1.989, -1.311, -0.627, 0.294, 0.788, 0.920, 0.997, 1.023, 1.037, 1.044, 1.051],
+    [-3.499, -1.981, -1.318, -0.643, 0.282, 0.799, 0.945, 1.035, 1.069, 1.087, 1.097, 1.107],
+    [-3.444, -1.972, -1.324, -0.660, 0.268, 0.808, 0.970, 1.075, 1.116, 1.140, 1.155, 1.170],
+    [-3.388, -1.962, -1.329, -0.675, 0.254, 0.817, 0.994, 1.116, 1.166, 1.197, 1.216, 1.238],
+    [-3.330, -1.951, -1.333, -0.691, 0.240, 0.825, 1.018, 1.157, 1.217, 1.256, 1.282, 1.313],
+    [-3.271, -1.938, -1.337, -0.705, 0.225, 0.832, 1.041, 1.198, 1.270, 1.318, 1.351, 1.394],
+    [-3.211, -1.925, -1.339, -0.719, 0.210, 0.838, 1.064, 1.240, 1.324, 1.383, 1.424, 1.482],
+    [-3.149, -1.910, -1.340, -0.733, 0.195, 0.844, 1.086, 1.282, 1.379, 1.449, 1.501, 1.577],
+    [-3.087, -1.894, -1.341, -0.745, 0.180, 0.848, 1.107, 1.324, 1.435, 1.518, 1.581, 1.678],
+    [-3.023, -1.877, -1.340, -0.758, 0.164, 0.852, 1.128, 1.366, 1.492, 1.588, 1.664, 1.786],
+    [-2.957, -1.859, -1.339, -0.769, 0.148, 0.854, 1.147, 1.407, 1.549, 1.660, 1.749, 1.899],
+    [-2.891, -1.839, -1.336, -0.780, 0.132, 0.856, 1.166, 1.448, 1.606, 1.733, 1.837, 2.017],
+    [-2.824, -1.819, -1.333, -0.790, 0.116, 0.857, 1.183, 1.489, 1.663, 1.806, 1.926, 2.141],
+    [-2.755, -1.797, -1.329, -0.800, 0.099, 0.857, 1.200, 1.528, 1.720, 1.880, 2.016, 2.268],
+    [-2.686, -1.774, -1.323, -0.808, 0.083, 0.857, 1.216, 1.567, 1.777, 1.955, 2.108, 2.399],
+    [-2.615, -1.750, -1.317, -0.816, 0.067, 0.855, 1.231, 1.606, 1.834, 2.029, 2.201, 2.533],
+    [-2.544, -1.726, -1.309, -0.824, 0.050, 0.853, 1.245, 1.643, 1.890, 2.104, 2.294, 2.669],
+    [-2.472, -1.700, -1.301, -0.830, 0.033, 0.850, 1.258, 1.680, 1.945, 2.178, 2.388, 2.808],
+    [-2.400, -1.673, -1.292, -0.836, 0.017, 0.846, 1.270, 1.716, 2.000, 2.253, 2.482, 2.948],
+    [-2.326, -1.645, -1.282, -0.842, 0.000, 0.842, 1.282, 1.751, 2.054, 2.326, 2.576, 3.090],
+    [-2.253, -1.616, -1.270, -0.846, -0.017, 0.836, 1.292, 1.785, 2.107, 2.400, 2.670, 3.233],
+    [-2.178, -1.586, -1.258, -0.850, -0.033, 0.830, 1.301, 1.818, 2.159, 2.472, 2.763, 3.377],
+    [-2.104, -1.555, -1.245, -0.853, -0.050, 0.824, 1.309, 1.849, 2.211, 2.544, 2.856, 3.521],
+    [-2.029, -1.524, -1.231, -0.855, -0.067, 0.816, 1.317, 1.880, 2.261, 2.615, 2.949, 3.666],
+    [-1.955, -1.491, -1.216, -0.857, -0.083, 0.808, 1.323, 1.910, 2.311, 2.686, 3.041, 3.811],
+    [-1.880, -1.458, -1.200, -0.857, -0.099, 0.800, 1.329, 1.939, 2.359, 2.755, 3.132, 3.956],
+    [-1.806, -1.423, -1.183, -0.857, -0.116, 0.790, 1.333, 1.967, 2.407, 2.824, 3.223, 4.100],
+    [-1.733, -1.389, -1.166, -0.856, -0.132, 0.780, 1.336, 1.993, 2.453, 2.891, 3.312, 4.244],
+    [-1.660, -1.353, -1.147, -0.854, -0.148, 0.769, 1.339, 2.018, 2.498, 2.957, 3.401, 4.388],
+    [-1.588, -1.317, -1.128, -0.852, -0.164, 0.758, 1.340, 2.043, 2.542, 3.023, 3.489, 4.531],
+    [-1.518, -1.280, -1.107, -0.848, -0.180, 0.745, 1.341, 2.066, 2.585, 3.087, 3.575, 4.673],
+    [-1.449, -1.243, -1.086, -0.844, -0.195, 0.733, 1.340, 2.088, 2.626, 3.149, 3.661, 4.815],
+    [-1.383, -1.206, -1.064, -0.838, -0.210, 0.719, 1.339, 2.108, 2.667, 3.211, 3.745, 4.955],
+    [-1.318, -1.168, -1.041, -0.832, -0.225, 0.705, 1.337, 2.128, 2.706, 3.271, 3.828, 5.095],
+    [-1.256, -1.131, -1.018, -0.825, -0.240, 0.691, 1.333, 2.146, 2.743, 3.330, 3.910, 5.234],
+    [-1.197, -1.093, -0.994, -0.817, -0.254, 0.675, 1.329, 2.163, 2.780, 3.388, 3.990, 5.371],
+    [-1.140, -1.056, -0.970, -0.808, -0.268, 0.660, 1.324, 2.179, 2.815, 3.444, 4.069, 5.507],
+    [-1.087, -1.020, -0.945, -0.799, -0.282, 0.643, 1.318, 2.193, 2.848, 3.499, 4.147, 5.642],
+    [-1.037, -0.984, -0.920, -0.788, -0.294, 0.627, 1.311, 2.207, 2.881, 3.553, 4.223, 5.775],
+    [-0.990, -0.949, -0.895, -0.777, -0.307, 0.609, 1.303, 2.219, 2.912, 3.605, 4.298, 5.908],
+    [-0.946, -0.915, -0.869, -0.765, -0.319, 0.592, 1.294, 2.230, 2.942, 3.656, 4.372, 6.039],
+    [-0.905, -0.882, -0.844, -0.752, -0.330, 0.574, 1.284, 2.240, 2.970, 3.705, 4.444, 6.168],
+    [-0.867, -0.850, -0.819, -0.739, -0.341, 0.555, 1.274, 2.248, 2.997, 3.753, 4.515, 6.296],
+    [-0.832, -0.819, -0.795, -0.725, -0.351, 0.537, 1.262, 2.256, 3.023, 3.800, 4.584, 6.423],
+    [-0.799, -0.790, -0.771, -0.711, -0.360, 0.518, 1.250, 2.262, 3.048, 3.845, 4.652, 6.548],
+    [-0.769, -0.762, -0.747, -0.696, -0.369, 0.499, 1.238, 2.267, 3.071, 3.889, 4.718, 6.672],
+    [-0.740, -0.736, -0.724, -0.681, -0.376, 0.479, 1.224, 2.272, 3.093, 3.932, 4.783, 6.794],
+    [-0.714, -0.711, -0.702, -0.666, -0.384, 0.460, 1.210, 2.275, 3.114, 3.973, 4.847, 6.915],
+    [-0.690, -0.688, -0.681, -0.651, -0.390, 0.440, 1.195, 2.277, 3.134, 4.013, 4.909, 7.034],
+    [-0.667, -0.665, -0.660, -0.636, -0.396, 0.420, 1.180, 2.278, 3.152, 4.051, 4.970, 7.152]
+]
+
+_INDEX_ST = np.arange(-30, 31, 1)/10
+
+_COL_ST = np.array([99, 95, 90, 80, 50, 20, 10, 4, 2, 1, 0.5, 0.1])/100
+
+t_pearson3_st = pd.DataFrame(data=_DATA_ST, index=_INDEX_ST, columns=_COL_ST)
+
+# Dari buku Limantara hal. 107-109
+# Tabel Distribsi log Pearson Tipe III Nilai G
+# Untuk Cs Positif & Negatif
+
+# KODE: LM
+
+_DATA_LM = [
+    [-0.667, -0.665, -0.660, -0.636, -0.396, 0.420, 1.180, 2.278, 3.152, 4.061, 4.970],
+    [-0.690, -0.688, -0.681, -0.651, -0.390, 0.440, 1.196, 2.277, 3.134, 4.013, 4.909],
+    [-0.714, -0.711, -0.702, -0.666, -0.384, 0.460, 1.210, 2.275, 3.114, 3.973, 4.847],
+    [-0.740, -0.736, -0.724, -0.681, -0.376, 0.479, 1.224, 2.272, 3.097, 3.932, 4.783],
+    [-0.769, -0.762, -0.747, -0.695, -0.368, 0.499, 1.238, 2.267, 3.071, 3.889, 4.718],
+    [-0.799, -0.790, -0.771, -0.711, -0.360, 0.518, 1.250, 2.262, 3.048, 3.845, 4.652],
+    [-0.832, -0.819, -0.795, -0.725, -0.351, 0.537, 1.262, 2.256, 3.029, 3.800, 4.584],
+    [-0.867, -0.850, -0.819, -0.739, -0.341, 0.555, 1.274, 2.248, 2.997, 3.753, 4.515],
+    [-0.905, -0.882, -0.844, -0.752, -0.330, 0.574, 1.284, 2.240, 2.970, 3.705, 4.454],
+    [-0.946, -0.914, -0.869, -0.765, -0.319, 0.592, 1.294, 2.230, 2.942, 3.656, 4.372],
+    [-0.990, -0.949, -0.896, -0.777, -0.307, 0.609, 1.302, 2.219, 2.912, 3.605, 4.298],
+    [-1.037, -0.984, -0.920, -0.788, -0.294, 0.627, 1.310, 2.207, 2.881, 3.553, 4.223],
+    [-1.087, -1.020, -0.945, -0.799, -0.282, 0.643, 1.318, 2.193, 2.848, 3.499, 4.147],
+    [-1.140, -1.056, -0.970, -0.808, -0.268, 0.660, 1.324, 2.179, 2.815, 3.444, 4.069],
+    [-1.197, -1.093, -0.994, -0.817, -0.254, 0.675, 1.329, 2.163, 2.780, 3.388, 3.990],
+    [-1.256, -1.131, -1.018, -0.825, -0.240, 0.690, 1.333, 2.146, 2.745, 3.330, 3.910],
+    [-1.318, -1.163, -1.041, -0.832, -0.225, 0.705, 1.337, 2.128, 2.706, 3.271, 3.828],
+    [-1.388, -1.206, -1.064, -0.838, -0.210, 0.719, 1.339, 2.108, 2.666, 3.211, 3.745],
+    [-1.449, -1.243, -1.086, -0.844, -0.195, 0.732, 1.340, 2.087, 2.626, 3.149, 3.661],
+    [-1.518, -1.280, -1.107, -0.848, -0.180, 0.745, 1.341, 2.066, 2.585, 3.087, 3.575],
+    [-1.588, -1.317, -1.128, -0.852, -0.164, 0.758, 1.340, 2.043, 2.542, 3.022, 3.489],
+    [-1.660, -1.353, -1.147, -0.854, -0.148, 0.769, 1.339, 2.018, 2.498, 2.967, 3.401],
+    [-1.733, -1.388, -1.166, -0.856, -0.132, 0.780, 1.336, 1.993, 2.453, 2.891, 3.312],
+    [-1.806, -1.423, -1.183, -0.857, -0.116, 0.790, 1.333, 1.967, 2.407, 2.824, 3.223],
+    [-1.880, -1.458, -1.200, -0.857, -0.099, 0.800, 1.328, 1.939, 2.359, 2.755, 3.123],
+    [-1.965, -1.491, -1.216, -0.856, -0.083, 0.808, 1.323, 1.910, 2.311, 2.686, 3.041],
+    [-2.029, -1.524, -1.231, -0.855, -0.066, 0.816, 1.317, 1.880, 2.261, 2.615, 2.949],
+    [-2.104, -1.555, -1.245, -0.853, -0.050, 0.824, 1.309, 1.849, 2.211, 2.544, 2.856],
+    [-2.175, -1.586, -1.258, -0.850, -0.033, 0.830, 1.301, 1.818, 2.159, 2.472, 2.763],
+    [-2.225, -1.616, -1.270, -0.846, -0.017, 0.836, 1.292, 1.785, 2.107, 2.400, 2.670],
+    [-2.326, -1.645, -1.282, -0.842, 0.000, 0.842, 1.282, 1.751, 2.064, 2.064, 2.576],
+    [-2.400, -1.673, -1.292, -0.836, 0.017, 0.846, 1.270, 1.716, 2.000, 2.252, 2.482],
+    [-2.472, -1.700, -1.301, -0.830, 0.033, 0.850, 1.258, 1.680, 1.945, 2.178, 2.388],
+    [-2.544, -1.762, -1.309, -0.824, 0.050, 0.853, 1.245, 0.163, 1.890, 2.104, 2.294],
+    [-2.615, -1.750, -1.317, -0.816, 0.066, 0.855, 1.231, 1.606, 1.834, 2.029, 2.201],
+    [-2.686, -1.774, -1.323, -0.808, 0.083, 0.856, 1.216, 1.567, 1.777, 1.955, 2.108],
+    [-2.755, -1.797, -1.328, -0.800, 0.099, 0.857, 1.200, 1.528, 1.720, 1.880, 2.016],
+    [-2.824, -1.819, -1.333, -0.790, 0.116, 0.857, 1.183, 1.488, 1.633, 1.800, 1.936],
+    [-2.891, -1.839, -1.336, -0.780, 0.132, 0.856, 1.166, 1.484, 1.608, 1.733, 1.837],
+    [-2.957, -1.858, -1.339, -0.769, 0.148, 0.854, 1.147, 1.407, 1.549, 1.660, 1.749],
+    [-3.022, -1.877, -1.340, -0.758, 0.164, 0.852, 1.108, 1.366, 1.492, 1.588, 1.664],
+    [-3.087, -1.894, -1.341, -0.745, 0.180, 0.848, 1.107, 1.324, 1.435, 1.518, 1.581],
+    [-3.149, -1.910, -1.340, -0.732, 0.195, 0.844, 1.086, 1.282, 1.379, 1.449, 1.501],
+    [-3.211, -1.925, -1.339, -0.719, 0.210, 0.838, 1.064, 1.240, 1.324, 1.383, 1.424],
+    [-3.271, -1.938, -1.337, -0.705, 0.225, 0.832, 1.041, 1.196, 1.270, 1.316, 1.351],
+    [-3.330, -1.961, -1.333, -0.690, 0.240, 0.825, 1.018, 1.157, 1.217, 1.256, 1.282],
+    [-3.388, -1.962, -1.329, -0.675, 0.254, 0.817, 0.994, 1.116, 1.168, 1.197, 1.216],
+    [-3.444, -1.972, -1.324, -0.660, 0.268, 0.808, 0.970, 1.075, 1.116, 1.140, 1.155],
+    [-3.499, -1.981, -1.318, -0.643, 0.282, 0.799, 0.945, 1.035, 1.069, 1.087, 1.097],
+    [-3.533, -1.989, -1.310, -0.627, 0.294, 0.788, 0.920, 0.996, 1.023, 1.037, 1.044],
+    [-3.605, -1.996, -1.302, -0.609, 0.307, 0.777, 0.895, 0.969, 0.980, 0.990, 0.995],
+    [-3.656, -2.001, -1.294, -0.592, 0.319, 0.765, 0.869, 0.923, 0.939, 0.346, 0.949],
+    [-3.705, -2.006, -1.284, -0.574, 0.330, 0.732, 0.849, 0.888, 0.900, 0.905, 0.907],
+    [-3.753, -2.009, -1.274, -0.555, 0.341, 0.739, 0.819, 0.855, 0.864, 0.867, 0.869],
+    [-3.800, -2.011, -1.262, -0.537, 0.351, 0.725, 0.795, 0.823, 0.830, 0.832, 0.833],
+    [-3.845, -2.012, -1.250, -0.518, 0.360, 0.711, 0.771, 0.793, 0.796, 0.799, 0.800],
+    [-3.889, -2.013, -1.238, -0.499, 0.368, 0.696, 0.747, 0.764, 0.767, 0.769, 0.769],
+    [-3.932, -2.011, -1.224, -0.479, 0.376, 0.681, 0.724, 0.738, 0.740, 0.740, 0.741],
+    [-3.973, -2.010, -1.210, -0.460, 0.384, 0.666, 0.702, 0.712, 0.714, 0.734, 0.714],
+    [-4.013, -2.007, -1.195, -0.440, 0.330, 0.651, 0.681, 0.683, 0.689, 0.690, 0.690],
+    [-4.051, -2.003, -1.180, -0.420, 0.390, 0.636, 0.660, 0.666, 0.666, 0.667, 0.667]
+]
+
+_INDEX_LM = np.arange(30, -31, -1) / 10
+
+_COL_LM =  np.array([99, 95, 90, 80, 50, 20, 10, 4, 2, 1, 0.5])/100
+
+t_pearson3_lm = pd.DataFrame(data=_DATA_LM, index=_INDEX_LM, columns=_COL_LM)
+
+# KODE FUNGSI INTERPOLASI DARI TABEL
+
+def _func_interp_bivariate(df):
+    "Membuat fungsi dari tabel untuk interpolasi bilinear"
+    table = df[df.columns.sort_values()].sort_index().copy()
+
+    x = table.index
+    y = table.columns
+    z = table.to_numpy()
+
+    # penggunaan kx=1, ky=1 untuk interpolasi linear antara 2 titik
+    # tidak menggunakan (cubic) spline interpolation
+    return interpolate.RectBivariateSpline(x, y, z, kx=1, ky=1)
+
+def _as_value(x):
+    x = np.around(x, 4)
+    return x.flatten() if x.size > 1 else x.item()
+
+
+def find_K(prob, skew_log, source='scipy'):
+    "Mencari nilai K berdasarkan probabilitas dan skewness logaritmik"
+    prob = np.array(prob)
+    if source.lower() == 'scipy':
+        #ref: https://github.com/hidrokit/hidrokit/discussions/156
+        if skew_log >= 0:
+            return np.around(stats.pearson3.ppf(1-prob, skew_log), 4)
+        else:
+            return np.around(stats.pearson3.ppf(prob, skew_log), 4)
+    elif source.lower() == 'soetopo':
+        func_pearson3_st = _func_interp_bivariate(t_pearson3_st)
+        return _as_value(func_pearson3_st(skew_log, prob, grid=False))
+    elif source.lower() == 'soewarno':
+        func_pearson3_sw = _func_interp_bivariate(t_pearson3_sw)
+        return _as_value(func_pearson3_sw(skew_log, prob, grid=False))
+    elif source.lower() == 'limantara':
+        func_pearson3_lm = _func_interp_bivariate(t_pearson3_lm)
+        return _as_value(func_pearson3_lm(skew_log, prob, grid=False))
+
+def calc_x_lp3(x, return_period=[5], source='scipy', show_stat=False):
+    "Menghitung besar X dengan kala ulang tertentu"
+    y = np.log10(x)
+    y_mean = np.mean(y)
+    y_std = np.std(y, ddof=1)
+    y_skew = stats.skew(y, bias=False)
+
+    prob = 1 / np.array(return_period)
+    k = find_K(prob, y_skew, source=source)
+
+    if show_stat:
+        print(f'y_mean = {y_mean:.5f}')
+        print(f'y_std = {y_std:.5f}')
+        print(f'y_skew = {y_skew:.5f}')
+        print(f'k = {k}')
+    
+    val_y = y_mean + k * y_std
+    val_x = np.power(10, val_y)
+    return val_x
+
+def freq_logpearson3(
+    df, col=None, 
+    return_period=[2, 5, 10, 20, 25, 50, 100], source='scipy', show_stat=False,
+    col_name='Log Pearson III', index_name='Kala Ulang'
+    ):
+
+    col = df.columns[0] if col is None else col
+
+    x = df[col].copy()
+
+    arr = calc_x_lp3(
+        x, return_period=return_period, show_stat=show_stat,
+        source=source
+    )
+
+    result = pd.DataFrame(
+        data=arr, index=return_period, columns=[col_name]
+    )
+
+    result.index.name = index_name
+    return result
+
+dict_table_source = {
+    'soewarno': t_pearson3_sw,
+    'soetopo': t_pearson3_st,
+    'limantara': t_pearson3_lm
+}
+
+def _find_prob_in_table(k, skew_log, table):
+    func_table = _func_interp_bivariate(table)
+    y = table.columns
+    x = func_table(skew_log, y, grid=False)
+    func_prob = interpolate.interp1d(x, y, kind='linear')
+    return _as_value(func_prob(k))
+
+def _calc_prob_in_table(k, skew_log, source='soewarno'):
+    if source.lower() in dict_table_source.keys():
+        return 1 - _find_prob_in_table(
+            k, skew_log, dict_table_source[source.lower()]
+        )
+   
+def calc_prob(k, skew_log, source='scipy'):
+    if source.lower() == 'scipy':
+        if skew_log >= 0:
+            return 1 - (1 - stats.pearson3.cdf(k, skew_log))
+        else:
+            return 1 - stats.pearson3.cdf(k, skew_log)
+    if source.lower() in dict_table_source.keys():
+        return _calc_prob_in_table(k, skew_log, source.lower())
diff --git a/hidrokit/contrib/taruma/hk127.py b/hidrokit/contrib/taruma/hk127.py
new file mode 100644
index 0000000..1eb43c9
--- /dev/null
+++ b/hidrokit/contrib/taruma/hk127.py
@@ -0,0 +1,440 @@
+"""manual:
+https://gist.github.com/taruma/ffa77e6f50a19fa5d05ab10e27d3266a"""
+
+import numpy as np
+import pandas as pd
+from scipy import stats
+
+# tabel dari gumbel
+# Statistics of Extremes oleh Gumbel p.228
+
+# KODE: GB
+
+_DATA_GB = [
+    [0.48430, 0.90430],
+    [0.49020, 0.92880],
+    [0.49520, 0.94970],
+    [0.49960, 0.96760],
+    [0.50350, 0.98330],
+    [0.50700, 0.99720],
+    [0.51000, 1.00950],
+    [0.51280, 1.02057],
+    [0.51570, 1.03160],
+    [0.51810, 1.04110],
+    [0.52020, 1.04930],
+    [0.52200, 1.05660],
+    [0.52355, 1.06283],
+    [0.52520, 1.06960],
+    [0.52680, 1.07540],
+    [0.52830, 1.08110],
+    [0.52960, 1.08640],
+    [0.53086, 1.09145],
+    [0.53200, 1.09610],
+    [0.53320, 1.10040],
+    [0.53430, 1.10470],
+    [0.53530, 1.10860],
+    [0.53622, 1.11238],
+    [0.53710, 1.11590],
+    [0.53800, 1.11930],
+    [0.53880, 1.12260],
+    [0.53960, 1.12550],
+    [0.54034, 1.12847],
+    [0.54100, 1.13130],
+    [0.54180, 1.13390],
+    [0.54240, 1.13630],
+    [0.54300, 1.13880],
+    [0.54362, 1.14132],
+    [0.54420, 1.14360],
+    [0.54480, 1.14580],
+    [0.54530, 1.14800],
+    [0.54580, 1.14990],
+    [0.54630, 1.15185],
+    [0.54680, 1.15380],
+    [0.54730, 1.15570],
+    [0.54770, 1.15740],
+    [0.54810, 1.15900],
+    [0.54854, 1.16066],
+    [0.54890, 1.16230],
+    [0.54930, 1.16380],
+    [0.54970, 1.16530],
+    [0.55010, 1.16670],
+    [0.55040, 1.16810],
+    [0.55080, 1.16960],
+    [0.55110, 1.17080],
+    [0.55150, 1.17210],
+    [0.55180, 1.17340],
+    [0.55208, 1.17467],
+    [0.55270, 1.17700],
+    [0.55330, 1.17930],
+    [0.55380, 1.18140],
+    [0.55430, 1.18340],
+    [0.55477, 1.18536],
+    [0.55520, 1.18730],
+    [0.55570, 1.18900],
+    [0.55610, 1.19060],
+    [0.55650, 1.19230],
+    [0.55688, 1.19382],
+    [0.55720, 1.19530],
+    [0.55760, 1.19670],
+    [0.55800, 1.19800],
+    [0.55830, 1.19940],
+    [0.55860, 1.20073],
+    [0.55890, 1.20200],
+    [0.55920, 1.20320],
+    [0.55950, 1.20440],
+    [0.55980, 1.20550],
+    [0.56002, 1.20649],
+    [0.56461, 1.22534],
+    [0.56715, 1.23598],
+    [0.56878, 1.24292],
+    [0.56993, 1.24786],
+    [0.57144, 1.25450],
+    [0.57240, 1.25880],
+    [0.57377, 1.26506],
+    [0.57450, 1.26851]
+]
+
+_INDEX_GB = (
+    list(range(8, 61)) +
+    list(range(62, 101, 2)) +
+    list(range(150, 301, 50)) +
+    [400, 500, 750, 1000]
+)
+
+_COL_GB = ['yn', 'sn']
+
+t_gumbel_gb = pd.DataFrame(
+    data=_DATA_GB, index=_INDEX_GB, columns=_COL_GB
+)
+
+# tabel dari soewarno
+# Tabel 3.11A & 3.11B p.129-130
+
+# KODE: GB
+
+_DATA_SW = [
+    [0.4592, 0.9496],
+    [0.4996, 0.9676],
+    [0.5053, 0.9933],
+    [0.5070, 0.9971],
+    [0.5100, 1.0095],
+    [0.5128, 1.0206],
+    [0.5157, 1.0316],
+    [0.5181, 1.0411],
+    [0.5202, 1.0493],
+    [0.5220, 1.0565],
+    [0.5236, 1.0628],
+    [0.5252, 1.0696],
+    [0.5268, 1.0754],
+    [0.5283, 1.0811],
+    [0.5296, 1.0864],
+    [0.5309, 1.0915],
+    [0.5320, 1.1961],
+    [0.5332, 1.1004],
+    [0.5343, 1.1047],
+    [0.5353, 1.1086],
+    [0.5362, 1.1124],
+    [0.5371, 1.1159],
+    [0.5380, 1.1193],
+    [0.5388, 1.1226],
+    [0.5396, 1.1255],
+    [0.5402, 1.1285],
+    [0.5410, 1.1313],
+    [0.5418, 1.1339],
+    [0.5424, 1.1363],
+    [0.5430, 1.1388],
+    [0.5436, 1.1413],
+    [0.5442, 1.1436],
+    [0.5448, 1.1458],
+    [0.5453, 1.1480],
+    [0.5458, 1.1499],
+    [0.5463, 1.1519],
+    [0.5468, 1.1538],
+    [0.5473, 1.1557],
+    [0.5477, 1.1574],
+    [0.5481, 1.1590],
+    [0.5485, 1.1607],
+    [0.5489, 1.1623],
+    [0.5493, 1.1638],
+    [0.5497, 1.1658],
+    [0.5501, 1.1667],
+    [0.5504, 1.1681],
+    [0.5508, 1.1696],
+    [0.5511, 1.1708],
+    [0.5518, 1.1721],
+    [0.5518, 1.1734],
+    [0.5521, 1.1747],
+    [0.5524, 1.1759],
+    [0.5527, 1.1770],
+    [0.5530, 1.1782],
+    [0.5533, 1.1793],
+    [0.5535, 1.1803],
+    [0.5538, 1.1814],
+    [0.5540, 1.1824],
+    [0.5543, 1.1834],
+    [0.5545, 1.1844],
+    [0.5548, 1.1854],
+    [0.5550, 1.1863],
+    [0.5552, 1.1873],
+    [0.5555, 1.1881],
+    [0.5557, 1.1890],
+    [0.5559, 1.1898],
+    [0.5561, 1.1906],
+    [0.5563, 1.1915],
+    [0.5565, 1.1923],
+    [0.5567, 1.1930],
+    [0.5569, 1.1938],
+    [0.5570, 1.1945],
+    [0.5572, 1.1953],
+    [0.5574, 1.1959],
+    [0.5576, 1.1967],
+    [0.5578, 1.1973],
+    [0.5580, 1.1980],
+    [0.5581, 1.1987],
+    [0.5583, 1.1994],
+    [0.5585, 1.2001],
+    [0.5586, 1.2007],
+    [0.5587, 1.2013],
+    [0.5589, 1.2020],
+    [0.5591, 1.2026],
+    [0.5592, 1.2032],
+    [0.5593, 1.2038],
+    [0.5595, 1.2044],
+    [0.5596, 1.2049],
+    [0.5598, 1.2055],
+    [0.5599, 1.2060],
+    [0.5600, 1.2065],
+]
+
+_INDEX_SW = list(range(10, 101))
+
+_COL_SW = ['yn', 'sn']
+
+t_gumbel_sw = pd.DataFrame(
+    data=_DATA_SW, index=_INDEX_SW, columns=_COL_SW
+)
+
+# Tabel dari Soetopo hal. 98
+# Tabel 12.1 Yn dan Sn Gumbel 
+
+# KODE: ST
+
+_DATA_ST = [
+    [0.4843, 0.9043],
+    [0.4902, 0.9288],
+    [0.4952, 0.9497],
+    [0.4996, 0.9676],
+    [0.5035, 0.9833],
+    [0.5070, 0.9972],
+    [0.5100, 1.0095],
+    [0.5128, 1.0205],
+    [0.5157, 1.0316],
+    [0.5181, 1.0411],
+    [0.5202, 1.0493],
+    [0.5220, 1.0566],
+    [0.5235, 1.0628],
+    [0.5252, 1.0696],
+    [0.5268, 1.0754],
+    [0.5283, 1.0811],
+    [0.5296, 1.0864],
+    [0.5309, 1.0915],
+    [0.5320, 1.0961],
+    [0.5332, 1.1004],
+    [0.5343, 1.1047],
+    [0.5353, 1.1086],
+    [0.5362, 1.1124],
+    [0.5371, 1.1159],
+    [0.5380, 1.1193],
+    [0.5388, 1.1226],
+    [0.5396, 1.1255],
+    [0.5402, 1.1285],
+    [0.5410, 1.1313],
+    [0.5418, 1.1339],
+    [0.5424, 1.1363],
+    [0.5430, 1.1388],
+    [0.5436, 1.1413],
+    [0.5442, 1.1436],
+    [0.5448, 1.1458],
+    [0.5453, 1.1480],
+    [0.5458, 1.1499],
+    [0.5463, 1.1519],
+    [0.5468, 1.1538],
+    [0.5473, 1.1557],
+    [0.5477, 1.1574],
+    [0.5481, 1.1590],
+    [0.5485, 1.1607],
+    [0.5489, 1.1623],
+    [0.5493, 1.1638],
+    [0.5497, 1.1658],
+    [0.5501, 1.1667],
+    [0.5504, 1.1681],
+    [0.5508, 1.1696],
+    [0.5511, 1.1708],
+    [0.5515, 1.1721],
+    [0.5518, 1.1734],
+    [0.5521, 1.1747],
+    [0.5524, 1.1759],
+    [0.5527, 1.1770],
+    [0.5530, 1.1782],
+    [0.5533, 1.1793],
+    [0.5535, 1.1803],
+    [0.5538, 1.1814],
+    [0.5540, 1.1824],
+    [0.5543, 1.1834],
+    [0.5545, 1.1844],
+    [0.5548, 1.1854],
+    [0.5550, 1.1863],
+    [0.5552, 1.1873],
+    [0.5555, 1.1881],
+    [0.5557, 1.1890],
+    [0.5559, 1.1898],
+    [0.5561, 1.1906],
+    [0.5563, 1.1915],
+    [0.5565, 1.1923],
+    [0.5567, 1.1930],
+    [0.5569, 1.1938],
+    [0.5570, 1.1945],
+    [0.5572, 1.1953],
+    [0.5574, 1.1959],
+    [0.5576, 1.1967],
+    [0.5578, 1.1973],
+    [0.5580, 1.1980],
+    [0.5581, 1.1987],
+    [0.5583, 1.1994],
+    [0.5585, 1.2001],
+    [0.5586, 1.2007],
+    [0.5587, 1.2013],
+    [0.5589, 1.2020],
+    [0.5591, 1.2026],
+    [0.5592, 1.2032],
+    [0.5593, 1.2038],
+    [0.5595, 1.2044],
+    [0.5596, 1.2049],
+    [0.5598, 1.2055],
+    [0.5599, 1.2060],
+    [0.5600, 1.2065],
+]
+
+_INDEX_ST = list(range(8, 101))
+
+_COL_ST = ['yn', 'sn']
+
+t_gumbel_st = pd.DataFrame(
+    data=_DATA_ST, index=_INDEX_ST, columns=_COL_ST
+)
+
+def _find_in_table(val, table, y_col=None, x_col=None):
+    x = table.index if x_col is None else table[x_col]
+    y = table.iloc[:, 0] if y_col is None else table[y_col]
+    return np.interp(val, x, y)
+
+def _find_Yn_Sn(n, table):
+    yn = _find_in_table(n, table, y_col='yn')
+    sn = _find_in_table(n, table, y_col='sn')
+    return yn, sn
+
+def find_coef(n, source='gumbel'):
+    if source.lower() == 'gumbel':
+        return _find_Yn_Sn(n, t_gumbel_gb)
+    if source.lower() == 'soewarno':
+        return _find_Yn_Sn(n, t_gumbel_sw)
+    if source.lower() == 'soetopo':
+        return _find_Yn_Sn(n, t_gumbel_st)
+
+def calc_K(n, return_period, source='gumbel', show_stat=False):
+    return_period = np.array(return_period)
+
+    if source.lower() == 'scipy':
+        # todo: perhitungan probabilitasnya belum dapat dipastikan formulanya
+        prob = 1 - 1/return_period
+        # prob = 1 - np.log(return_period/(return_period-1))
+        return stats.gumbel_r.ppf(prob)
+    elif source.lower() == 'powell':
+        return -np.sqrt(6)/np.pi *(np.euler_gamma+np.log(np.log(return_period/(return_period-1))))
+    else:
+        # dibuku Soewarno dinyatakan T>=20 menggunakan
+        # ln(T), tapi dicontohnya tidak mengikuti formula tersebut
+        # jadi yang digunakan rumus umumnya saja.
+        # if source.lower() == 'soewarno':
+        #     yt = []
+        #     for t in return_period:
+        #         if t <= 20:
+        #             yt += [-np.log(-np.log((t - 1)/t))]
+        #         else:
+        #             yt += [np.log(t)]
+        #     yt = np.array(yt)
+        # else:
+        #     yt = -np.log(-np.log((return_period - 1)/return_period))
+
+        yn, sn = find_coef(n, source=source)
+        yt = -np.log(-np.log((return_period - 1)/return_period))
+        K = (yt - yn) / sn
+
+        if show_stat:
+            print(f'y_n = {yn}')
+            print(f's_n = {sn}')
+            print(f'y_t = {yt}')
+        return K
+
+def calc_x_gumbel(x, return_period=[5], source='gumbel', show_stat=False):
+
+    x_mean = np.mean(x)
+    x_std = np.std(x, ddof=1)
+    n = len(x)
+
+    k = calc_K(n, return_period, source=source, show_stat=show_stat)
+
+    if show_stat:
+        print(f'x_mean = {x_mean:.5f}')
+        print(f'x_std = {x_std:.5f}')
+        print(f'k = {k}')
+    
+    val_x = x_mean + k * x_std
+    return val_x
+
+def freq_gumbel(
+    df, col=None,
+    return_period=[2, 5, 10, 20, 25, 50, 100], source='gumbel', show_stat=False,
+    col_name='Gumbel', index_name='Kala Ulang'):
+
+    col = df.columns[0] if col is None else col
+
+    x = df[col].copy()
+
+    arr = calc_x_gumbel(
+        x, return_period=return_period, show_stat=show_stat,
+        source=source
+    )
+
+    result = pd.DataFrame(
+        data=arr, index=return_period, columns=[col_name]
+    )
+
+    result.index.name = index_name
+    return result
+
+def _calc_T(P):
+    return 1 / (1-np.exp(-np.exp(-P)))
+
+def _calc_prob_from_table(k, n, source='gumbel'):
+    yn, sn = find_coef(n, source=source)
+    P = k * sn + yn
+    T = _calc_T(P)
+    return np.around(1-1/T, 3)
+
+def calc_prob(k, n, source='gumbel'):
+    if source.lower() == 'gumbel':
+        return _calc_prob_from_table(k, n, source=source)
+    if source.lower() == 'soewarno':
+        return _calc_prob_from_table(k, n, source=source)
+    if source.lower() == 'soetopo':
+        return _calc_prob_from_table(k, n, source=source)
+    if source.lower() == 'scipy':
+        return stats.gumbel_r.cdf(k)
+    if source.lower() == 'powell':
+        # persamaan ini ditemukan menggunakan wolfram alpha
+        # x = e^(e^(-(π K)/sqrt(6) - p))/(e^(e^(-(π K)/sqrt(6) - p)) - 1)
+        _top = np.exp(np.exp(-(np.pi*k)/np.sqrt(6)-np.euler_gamma))
+        _bot = _top - 1
+        T = _top / _bot
+        return 1-1/T
diff --git a/hidrokit/contrib/taruma/hk140.py b/hidrokit/contrib/taruma/hk140.py
new file mode 100644
index 0000000..f3ef113
--- /dev/null
+++ b/hidrokit/contrib/taruma/hk140.py
@@ -0,0 +1,168 @@
+"""manual:
+https://gist.github.com/taruma/5d16baf90016d8a08c6870b674226691"""
+
+import numpy as np
+import pandas as pd
+from scipy import stats
+from hidrokit.contrib.taruma import hk172, hk124, hk127, hk126 
+
+frek_normal, frek_lognormal, frek_gumbel, frek_logpearson3 = hk172, hk124, hk127, hk126
+
+# tabel dari soetopo hal. 139
+# Tabel Nilai Kritis (Dcr) Untuk Uji Kolmogorov-Smirnov
+
+# KODE: ST
+
+_DATA_ST = [
+    [0.900, 0.925, 0.950, 0.975, 0.995],
+    [0.684, 0.726, 0.776, 0.842, 0.929],
+    [0.565, 0.597, 0.642, 0.708, 0.829],
+    [0.494, 0.525, 0.564, 0.624, 0.734],
+    [0.446, 0.474, 0.510, 0.563, 0.669],
+    [0.410, 0.436, 0.470, 0.521, 0.618],
+    [0.381, 0.405, 0.438, 0.486, 0.577],
+    [0.358, 0.381, 0.411, 0.457, 0.543],
+    [0.339, 0.360, 0.388, 0.432, 0.514],
+    [0.322, 0.342, 0.368, 0.409, 0.486],
+    [0.307, 0.326, 0.352, 0.391, 0.468],
+    [0.295, 0.313, 0.338, 0.375, 0.450],
+    [0.284, 0.302, 0.325, 0.361, 0.433],
+    [0.274, 0.292, 0.314, 0.349, 0.418],
+    [0.266, 0.283, 0.304, 0.338, 0.404],
+    [0.258, 0.274, 0.295, 0.328, 0.391],
+    [0.250, 0.266, 0.286, 0.318, 0.380],
+    [0.244, 0.259, 0.278, 0.309, 0.370],
+    [0.237, 0.252, 0.272, 0.301, 0.361],
+    [0.231, 0.246, 0.264, 0.294, 0.352],
+]
+
+_INDEX_ST = range(1, 21)
+
+_COL_ST = [0.2, 0.15, 0.1, 0.05, 0.01]
+
+t_dcr_st = pd.DataFrame(
+    data=_DATA_ST, index=_INDEX_ST, columns=_COL_ST
+)
+
+# tabel dari soewarno hal. 139
+# Tabel Nilai Kritis (Dcr) Untuk Uji Kolmogorov-Smirnov
+
+# KODE: SW
+
+_DATA_SW = [
+    [0.45, 0.51, 0.56, 0.67],
+    [0.32, 0.37, 0.41, 0.49],
+    [0.27, 0.3 , 0.34, 0.4 ],
+    [0.23, 0.26, 0.29, 0.35],
+    [0.21, 0.24, 0.26, 0.32],
+    [0.19, 0.22, 0.24, 0.29],
+    [0.18, 0.2 , 0.22, 0.27],
+    [0.17, 0.19, 0.21, 0.25],
+    [0.16, 0.18, 0.2 , 0.24],
+    [0.15, 0.17, 0.19, 0.23]
+]
+
+_INDEX_SW = range(5, 51, 5)
+
+_COL_SW = [0.2, 0.1, 0.05, 0.01]
+
+t_dcr_sw = pd.DataFrame(
+    data=_DATA_SW, index=_INDEX_SW, columns=_COL_SW
+)
+
+# KODE FUNGSI INTERPOLASI DARI TABEL
+
+from scipy import interpolate
+
+def _func_interp_bivariate(df):
+    "Membuat fungsi dari tabel untuk interpolasi bilinear"
+    table = df[df.columns.sort_values()].sort_index().copy()
+
+    x = table.index
+    y = table.columns
+    z = table.to_numpy()
+
+    # penggunaan kx=1, ky=1 untuk interpolasi linear antara 2 titik
+    # tidak menggunakan (cubic) spline interpolation
+    return interpolate.RectBivariateSpline(x, y, z, kx=1, ky=1)
+
+def _as_value(x, dec=4):
+    x = np.around(x, dec)
+    return x.flatten() if x.size > 1 else x.item()
+
+def _calc_k(x):
+    return (x - x.mean()) / x.std()
+
+table_source = {
+    'soewarno': t_dcr_sw,
+    'soetopo': t_dcr_st
+}
+
+anfrek = {
+    'normal': frek_normal,
+    'lognormal': frek_lognormal,
+    'gumbel': frek_gumbel,
+    'logpearson3': frek_logpearson3
+}
+
+def calc_dcr(alpha, n, source='scipy'):
+    alpha = np.array(alpha)
+    if source.lower() == 'scipy':
+        # ref: https://stackoverflow.com/questions/53509986/
+        return stats.ksone.ppf(1-alpha/2, n)
+    elif source.lower() in table_source.keys():
+        func_table = _func_interp_bivariate(table_source[source.lower()])
+        # untuk soewarno 2 angka dibelakang koma, dan soetopo = 3
+        dec = (source.lower() == 'soetopo') + 2
+        return _as_value(func_table(n, alpha, grid=False), dec)
+
+def kstest(
+    df, col=None, dist='normal', source_dist='scipy', 
+    alpha=0.05, source_dcr='scipy', show_stat=True, report='result'
+    ):
+
+    source_dist = 'gumbel' if dist.lower() == 'gumbel' else source_dist
+
+    col = df.columns[0] if col is None else col
+    data = df[[col]].copy()
+    n = len(data)
+    data = data.rename({col: 'x'}, axis=1)
+    data = data.sort_values('x')
+    data['no'] = np.arange(n) + 1
+
+    # w = weibull
+    data['p_w'] = data.no / (n+1)
+    
+    if dist.lower() in ['normal', 'gumbel']:
+        data['k'] = _calc_k(data.x)
+    if dist.lower() in ['lognormal', 'logpearson3']:
+        data['log_x'] = np.log10(data.x)
+        data['k'] = _calc_k(data.log_x)
+
+    func = anfrek[dist.lower()]
+
+    if dist.lower() in ['normal', 'lognormal']:
+        parameter = ()
+    elif dist.lower() == 'gumbel':
+        parameter = (n,)
+    elif dist.lower() == 'logpearson3':
+        parameter = (data.log_x.skew(),)
+    
+    # d = distribusi
+    data['p_d'] = func.calc_prob(data.k, source=source_dist, *parameter) 
+    data['d'] = (data.p_w - data.p_d).abs()
+    dmax = data.d.max()
+    dcr = calc_dcr(alpha, n, source=source_dcr)
+    result = int(dmax < dcr)
+    result_text = ['Distribusi Tidak Diterima', 'Distribusi Diterima']
+
+    if show_stat:
+        print(f'Periksa Kecocokan Distribusi {dist.title()}')
+        print(f'Delta Kritikal = {dcr:.5f}')
+        print(f'Delta Max = {dmax:.5f}')
+        print(f'Result (Dmax < Dcr) = {result_text[result]}')
+
+    if report.lower() == 'result':
+        return data['no x p_w p_d d'.split()]
+    elif report.lower() == 'full':
+        return data
diff --git a/hidrokit/contrib/taruma/hk141.py b/hidrokit/contrib/taruma/hk141.py
new file mode 100644
index 0000000..5a4dd64
--- /dev/null
+++ b/hidrokit/contrib/taruma/hk141.py
@@ -0,0 +1,179 @@
+"""manual:
+https://gist.github.com/taruma/e250ab2685ba5b4c8facbf498cfb5cd8"""
+
+import numpy as np
+import pandas as pd
+from scipy import stats
+from hidrokit.contrib.taruma import hk172, hk124, hk127, hk126 
+
+frek_normal, frek_lognormal, frek_gumbel, frek_logpearson3 = hk172, hk124, hk127, hk126
+
+# tabel dari limantara hal. 117
+# Tabel Nilai Kritis untuk Distribusi Chi Square (X^2)
+
+# KODE: LM
+
+_DATA_LM = [
+    [0.039, 0.016, 0.698, 0.393, 3.841, 5.024, 6.635, 7.879],
+    [0.100, 0.201, 0.506, 0.103, 5.991, 0.738, 9.210, 10.597],
+    [0.717, 0.115, 0.216, 0.352, 7.815, 9.348, 11.345, 12.838],
+    [0.207, 0.297, 0.484, 0.711, 9.488, 11.143, 13.277, 14.860],
+    [0.412, 0.554, 0.831, 1.145, 11.070, 12.832, 15.086, 16.750],
+    [0.676, 0.872, 1.237, 1.635, 12.592, 14.449, 16.812, 18.548],
+    [0.989, 1.239, 1.690, 2.167, 14.067, 16.013, 18.475, 20.278],
+    [1.344, 1.646, 2.180, 2.733, 15.507, 17.535, 20.090, 21.955],
+    [1.735, 2.088, 2.700, 3.325, 16.919, 19.023, 21.666, 23.589],
+    [2.156, 2.558, 3.247, 3.940, 18.307, 20.483, 23.209, 25.188],
+    [2.603, 3.053, 3.816, 4.575, 19.675, 21.920, 24.725, 26.757],
+    [3.074, 3.571, 4.404, 5.226, 21.026, 23.337, 26.217, 28.300],
+    [3.565, 4.107, 5.009, 5.892, 22.362, 24.736, 27.688, 29.819],
+    [4.075, 4.660, 5.629, 6.571, 23.685, 26.119, 29.141, 31.319],
+    [4.601, 5.229, 6.262, 7.261, 24.996, 27.488, 30.578, 32.801],
+    [5.142, 5.812, 6.908, 7.962, 26.296, 28.845, 32.000, 34.267],
+    [5.697, 6.408, 7.564, 8.672, 27.587, 30.191, 33.409, 35.718],
+    [6.265, 7.015, 8.231, 9.390, 28.869, 31.526, 34.805, 37.156],
+    [6.884, 7.633, 8.907, 10.117, 30.144, 32.852, 36.191, 38.582],
+    [7.434, 8.260, 9.591, 10.851, 31.410, 34.170, 37.566, 39.997],
+    [8.034, 8.897, 10.283, 11.591, 32.671, 35.479, 38.932, 41.401],
+    [8.643, 9.542, 10.982, 12.338, 33.924, 36.781, 40.289, 42.796],
+    [9.260, 10.196, 11.689, 13.091, 36.172, 38.076, 41.638, 44.181],
+    [9.886, 10.856, 12.401, 13.848, 36.415, 39.364, 42.980, 45.558],
+    [10.520, 11.524, 13.120, 14.611, 37.652, 40.646, 44.314, 46.928],
+    [11.160, 12.198, 13.844, 15.379, 38.885, 41.923, 45.642, 48.290],
+    [11.808, 12.879, 14.573, 16.151, 40.113, 43.194, 46.963, 49.645],
+    [12.461, 13.565, 15.308, 16.928, 41.337, 44.461, 48.278, 50.993],
+    [13.121, 14.256, 16.047, 17.708, 42.557, 45.722, 49.588, 52.336],
+    [13.787, 14.953, 16.791, 18.493, 43.773, 46.979, 50.892, 53.672],
+]
+
+_INDEX_LM = range(1, 31)
+
+_COL_LM = [0.995, .99, .975, .95, .05, .025, 0.01, 0.005]
+
+t_chi_lm = pd.DataFrame(
+    data=_DATA_LM, index=_INDEX_LM, columns=_COL_LM
+)
+
+from scipy import interpolate
+
+def _func_interp_bivariate(df):
+    "Membuat fungsi dari tabel untuk interpolasi bilinear"
+    table = df[df.columns.sort_values()].sort_index().copy()
+
+    x = table.index
+    y = table.columns
+    z = table.to_numpy()
+
+    # penggunaan kx=1, ky=1 untuk interpolasi linear antara 2 titik
+    # tidak menggunakan (cubic) spline interpolation
+    return interpolate.RectBivariateSpline(x, y, z, kx=1, ky=1)
+
+def _as_value(x, dec=4):
+    x = np.around(x, dec)
+    return x.flatten() if x.size > 1 else x.item()
+
+table_source = {
+    'limantara': t_chi_lm
+}
+
+anfrek = {
+    'normal': frek_normal.calc_x_normal,
+    'lognormal': frek_lognormal.calc_x_lognormal,
+    'gumbel': frek_gumbel.calc_x_gumbel,
+    'logpearson3': frek_logpearson3.calc_x_lp3,
+}
+
+def _calc_k(n):
+    return np.floor(1 + 3.22 * np.log10(n)).astype(int)
+
+def _calc_dk(k, m):
+    return k - 1 - m
+
+def calc_xcr(alpha, dk, source='scipy'):
+    alpha = np.array(alpha)
+
+    if source.lower() in table_source.keys():
+        func_table = _func_interp_bivariate(table_source[source.lower()])
+        return _as_value(func_table(dk, alpha, grid=False), 3)
+    if source.lower() == 'scipy':
+        #ref: https://stackoverflow.com/questions/32301698
+        return stats.chi2.isf(alpha, dk)
+
+def chisquare(
+    df, col=None, dist='normal', source_dist='scipy',
+    alpha=0.05, source_xcr='scipy', show_stat=True,
+    ):
+
+    source_dist = 'gumbel' if dist.lower() == 'gumbel' else source_dist
+
+    col = df.columns[0] if col is None else col
+    data = df[[col]].copy()
+    n = len(data)
+    data = data.rename({col: 'x'}, axis=1)
+
+    if dist.lower() in ['lognormal', 'logpearson3']:
+        data['log_x'] = np.log10(data.x)
+
+    k = _calc_k(n)
+    prob_class = 1 / k
+    prob_list = np.linspace(0, 1, k+1)[::-1]
+    prob_seq = prob_list[1:-1]
+
+    func = anfrek[dist.lower()]
+
+    T = 1 / prob_seq
+    val_x = func(data.x, return_period=T, source=source_dist)
+
+    # Chi Square Table
+    calc_df = pd.DataFrame()
+    min = data.x.min()
+    max = data.x.max()
+    seq_x = np.concatenate([[min], val_x, [max]])
+
+    calc_df['no'] = range(1, k+1)
+
+    class_text = []
+    for i in range(seq_x.size-1):
+        if i == 0:
+            class_text += [f'X <= {seq_x[i+1]:.4f}']
+        elif i == seq_x.size-2:
+            class_text += [f'X > {seq_x[i]:.4f}']
+        else:
+            class_text += [f'{seq_x[i]:.4f} < X <= {seq_x[i+1]:.4f}']
+    calc_df['batas_kelas'] = class_text
+
+    # calculate fe
+    fe = []
+    for i in range(seq_x.size-1):
+        if i == 0:
+            fe += [(data.x <= seq_x[i+1]).sum()]
+        elif i == seq_x.size-2:
+            fe += [(data.x > seq_x[i]).sum()]
+        else:
+            fe += [data.x.between(seq_x[i], seq_x[i+1], inclusive='right').sum()]
+    calc_df['fe'] = fe
+
+    ft = prob_class * n
+    calc_df['ft'] = [ft]*k
+
+    if dist.lower() in ['normal', 'gumbel', 'lognormal']:
+        dk = _calc_dk(k, 2)
+    elif dist.lower() in ['logpearson3']:
+        # di buku soetopo nilai m nya diberi angka 3
+        dk = _calc_dk(k, 2)
+
+    X_calc = np.sum(np.power(2, (calc_df.fe-calc_df.ft))/calc_df.ft)
+    X_critical = calc_xcr(alpha=alpha, dk=dk, source=source_xcr)
+    result = int(X_calc < X_critical)
+    result_text = ['Distribusi Tidak Diterima', 'Distribusi Diterima']
+    calc_df.set_index('no', inplace=True)
+
+    if show_stat:
+        print(f'Periksa Kecocokan Distribusi {dist.title()}')
+        print(f'Jumlah Kelas = {k}')
+        print(f'Dk = {dk}')
+        print(f'X^2_hitungan = {X_calc:.3f}')
+        print(f'X^2_kritis = {X_critical:.3f}')
+        print(f'Result (X2_calc < X2_cr) = {result_text[result]}')
+
+    return calc_df
diff --git a/hidrokit/contrib/taruma/hk151.py b/hidrokit/contrib/taruma/hk151.py
new file mode 100644
index 0000000..7d25012
--- /dev/null
+++ b/hidrokit/contrib/taruma/hk151.py
@@ -0,0 +1,97 @@
+"""manual:
+https://gist.github.com/taruma/7bf2e4e1601ab8390d9919043eb87682"""
+
+import numpy as np
+import pandas as pd
+
+_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'])
+
+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
diff --git a/hidrokit/contrib/taruma/hk158.py b/hidrokit/contrib/taruma/hk158.py
new file mode 100644
index 0000000..a17c13e
--- /dev/null
+++ b/hidrokit/contrib/taruma/hk158.py
@@ -0,0 +1,77 @@
+"""manual:
+https://gist.github.com/taruma/6a0b0f9dd26359f6832fe12bab30fdc7"""
+
+import numpy as np
+import pandas as pd
+
+def _var(x):
+    n = x.size
+    return ((x-x.mean())**2).sum()/(n-1)
+
+def _std(x):
+    return np.sqrt(_var(x))
+
+def _momen(x, r):
+    n = x.size
+    return 1/n * ((x-x.mean())**r).sum()
+
+def _skew(x):
+    n = x.size
+    return n**2 / ((n-1)*(n-2)) * _momen(x, 3) / _std(x)**3
+
+def _kurt(x):
+    n = x.size
+    return n**3 / ((n-1)*(n-2)*(n-3)) * _momen(x, 4) / _std(x)**4
+
+def _Cv(x):
+    return _std(x) / x.mean()
+
+def calc_coef(x):
+    """Return (Cv, Cs, Ck)"""
+    return (_Cv(x), _skew(x), _kurt(x))
+
+def check_distribution(x, show_stat=False, show_detail=False):
+
+    Cv, Cs, Ck = calc_coef(x)
+
+    if show_stat:
+        print(
+            f'Cv = {Cv:.5f}',
+            f'Cs = {Cs:.5f}',
+            f'Ck = {Ck:.5f}',
+            sep='\n', end='\n'
+        )
+
+    b_normal = True if np.isclose(Cs, 0, atol=0.1) and np.isclose(Ck, 3, atol=0.1) else False
+    b_lognormal = True if np.isclose(Cs, 3, atol=0.1) and np.greater(Cs, 0) and np.isclose(Cs, 3*Cv, atol=0.1) else False
+    b_gumbel = True if np.isclose(Cs, 1.1396, atol=0.0001) and np.isclose(Ck, 5.4002, atol=0.0001) else False
+    b_logpearson = True #selalu benar terlepas nilai Cv, Cs, Ck
+
+    _kriteria = lambda x: "Memenuhi" if x else "Tidak Memenuhi"
+
+    print(
+        f'{"Normal":<20}: {_kriteria(b_normal)}',
+        f'{"Log Normal":<20}: {_kriteria(b_lognormal)}',
+        f'{"Gumbel Tipe I":<20}: {_kriteria(b_gumbel)}',
+        f'{"Log Pearson Tipe III":<20}: {_kriteria(b_logpearson)}',
+        sep='\n', end='\n'
+    )
+
+    if show_detail:
+        print(
+            '-----------------------------------------------',
+            '> Distribusi Normal [syarat](nilai)',
+            f'[Cs ~ 0](Cs = {Cs:.5f})',
+            f'[Ck ~ 3](Ck = {Ck:.5f})',
+            '> Log Normal',
+            f'[Cs ~ 3](Cs = {Cs:.5f})',
+            f'[Cs > 0](Cs = {Cs:.5f})',
+            f'[Cs ~ 3Cv](Cs = {Cs:.5f} ~ 3Cv = {3*Cv:.5f})',
+            '> Gumbel Tipe I',
+            f'[Cs ~ 1.1396](Cs = {Cs:.5f})',
+            f'[Ck ~ 5.4002](Ck = {Ck:.5f})',
+            '> Log Pearson Tipe III',
+            'Tidak memiliki ciri khas (Cs/Ck/Cv = Bebas)',
+            '-----------------------------------------------',
+            sep='\n', end='\n'
+        )
diff --git a/hidrokit/contrib/taruma/hk172.py b/hidrokit/contrib/taruma/hk172.py
new file mode 100644
index 0000000..78b526b
--- /dev/null
+++ b/hidrokit/contrib/taruma/hk172.py
@@ -0,0 +1,101 @@
+"""manual:
+https://gist.github.com/taruma/91b9fcd8fb92c12f4ea2639320ead116"""
+
+import numpy as np
+import pandas as pd
+from scipy import stats
+
+# Tabel Nilai Variabel Reduksi Gauss
+# Dari buku hidrologi: Aplikasi Metode Statistik untuk Analisa Data. hal.119
+
+# KODE: SW
+
+_DATA_SW = [
+    [1.001, 0.999, -3.050],
+    [1.005, 0.995, -2.580],
+    [1.010, 0.990, -2.330],
+    [1.050, 0.950, -1.640],
+    [1.110, 0.900, -1.280],
+    [1.250, 0.800, -0.840],
+    [1.330, 0.750, -0.670],
+    [1.430, 0.700, -0.520],
+    [1.670, 0.600, -0.250],
+    [2.000, 0.500, 0.000],
+    [2.500, 0.400, 0.250],
+    [3.330, 0.300, 0.520],
+    [4.000, 0.250, 0.670],
+    [5.000, 0.200, 0.840],
+    [10.000, 0.100, 1.280],
+    [20.000, 0.050, 1.640],
+    [50.000, 0.200, 2.050],
+    [100.000, 0.010, 2.330],
+    [200.000, 0.005, 2.580],
+    [500.000, 0.002, 2.880],
+    [1000.000, 0.001, 3.090],
+]
+
+_COL_SW = ['periode_ulang', 'peluang', 'k']
+
+t_normal_sw = pd.DataFrame(
+    data=_DATA_SW, columns=_COL_SW
+)
+
+def _find_k_in_table(return_period, table):
+    x = table.periode_ulang
+    y = table.k
+    return np.interp(return_period, x, y)
+
+def find_K(return_period, source='scipy'):
+    if source.lower() == 'soewarno':
+        return _find_k_in_table(return_period, t_normal_sw)
+    elif source.lower() == 'scipy':
+        return_period = np.array(return_period)
+        return stats.norm.ppf(1 - 1/return_period)
+
+def calc_x_normal(x, return_period=[5], source='scipy', show_stat=False):
+    return_period = np.array(return_period)
+    x = np.array(x)
+    x_mean = np.mean(x)
+    x_std = np.std(x, ddof=1)
+    n = x.size
+
+    k = find_K(return_period, source=source)
+
+    if show_stat:
+        print(f'x_mean = {x_mean:.5f}')
+        print(f'x_std = {x_std:.5f}')
+        print(f'k = {k}')
+
+    val_x = x_mean + k * x_std
+    return val_x
+
+def freq_normal(
+    df, col=None,
+    return_period=[2, 5, 10, 20, 25, 50, 100], show_stat=False, source='scipy',
+    col_name='Normal', index_name='Kala Ulang'):
+
+    col = df.columns[0] if col is None else col
+
+    x = df[col].copy()
+
+    arr = calc_x_normal(
+        x, return_period=return_period, show_stat=show_stat, source=source)
+
+    result = pd.DataFrame(
+        data=arr, index=return_period, columns=[col_name]
+    )
+
+    result.index.name = index_name
+    return result
+    
+def _calc_prob_in_table(k, table):
+    x = table.k
+    y = table.peluang
+    return np.interp(k, x, y)
+
+def calc_prob(k, source='scipy'):
+    if source.lower() == 'soewarno':
+        k = np.array(k)
+        return 1 - _calc_prob_in_table(k, t_normal_sw)
+    elif source.lower() == 'scipy':
+        return stats.norm.cdf(k)
diff --git a/hidrokit/contrib/taruma/hk88.py b/hidrokit/contrib/taruma/hk88.py
index 1db9ffb..a9b51b1 100644
--- a/hidrokit/contrib/taruma/hk88.py
+++ b/hidrokit/contrib/taruma/hk88.py
@@ -51,16 +51,25 @@ def _data_from_sheet(df, station_name, as_df=True):
         return frames
 
 
-def read_workbook(io, stations, as_df=True):
-    """Read dataset from single file based on stations"""
+def read_workbook(io, stations=None, ignore_str='_', as_df=True):
+    """Read dataset from workbook"""
     excel = pd.ExcelFile(io)
 
-    data = []
+    data = {}
+    sheet_names = excel.sheet_names
+    if stations is None:
+        stations = []
+        for sheet in sheet_names:
+            if not sheet.startswith(ignore_str):
+                stations.append(sheet)
+    else:
+        stations = [stations] if isinstance(stations, str) else stations
+
     for station in stations:
-        df = pd.read_excel(excel, sheet_name=station, header=None)
-        data.append(_data_from_sheet(df, station))
+        df = excel.parse(sheet_name=station, header=None)
+        data[station] = _data_from_sheet(df, station)
 
     if as_df:
-        return pd.concat(data, sort=True, axis=1)
+        return pd.concat(data.values(), sort=True, axis=1)
     else:
         return data
diff --git a/hidrokit/contrib/taruma/hk98.py b/hidrokit/contrib/taruma/hk98.py
index bf35b8d..6c58d64 100644
--- a/hidrokit/contrib/taruma/hk98.py
+++ b/hidrokit/contrib/taruma/hk98.py
@@ -27,14 +27,15 @@ def summary_station(dataset, column, ufunc, ufunc_col, n_days='MS'):
     )
 
 
-def summary_all(dataset, ufunc, ufunc_col, columns=None, n_days='MS'):
+def summary_all(dataset, ufunc, ufunc_col, columns=None, n_days='MS', verbose=False):
     res = []
 
     columns = columns if columns is not None else list(dataset.columns)
     columns = columns if isinstance(columns, (list, tuple)) else [columns]
 
     for column in columns:
-        print('PROCESSING:', column)
+        if verbose:
+            print('PROCESSING:', column)
         res.append(
             summary_station(dataset, column, ufunc, ufunc_col, n_days=n_days)
         )
diff --git a/hidrokit/contrib/taruma/ujidist.py b/hidrokit/contrib/taruma/ujidist.py
new file mode 100644
index 0000000..0e00c8f
--- /dev/null
+++ b/hidrokit/contrib/taruma/ujidist.py
@@ -0,0 +1,8 @@
+"""
+modul rekap untuk uji kecocokan distribusi
+"""
+
+from hidrokit.contrib.taruma import hk140, hk141
+
+uji_kstest = hk140.kstest
+uji_chisquare = hk141.chisquare
diff --git a/requirements.txt b/requirements.txt
index 0a8218e..315e10c 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -2,3 +2,4 @@ pandas
 numpy
 matplotlib
 xlrd
+scipy
diff --git a/setup.py b/setup.py
index 1cf2ef5..a4a4ec1 100644
--- a/setup.py
+++ b/setup.py
@@ -1,3 +1,7 @@
+"""
+Setup files
+"""
+
 #!/usr/bin/env python
 # -*- coding: utf-8 -*-
 
@@ -11,9 +15,9 @@
 NAME = 'hidrokit'
 DESCRIPTION = 'analisis hidrologi dengan python'
 URL = 'https://github.com/hidrokit/hidrokit'
-EMAIL = 'timhidrokit@gmail.com'
-AUTHOR = 'Hidrokit'
-REQUIRES_PYTHON = '>=3.5.0'
+EMAIL = 'hi@taruma.info'
+AUTHOR = 'Taruma Sakti Megariansyah'
+REQUIRES_PYTHON = '>=3.6.0'
 VERSION = ''
 
 # What packages are required for this module to be executed?
@@ -90,7 +94,6 @@
         # Python Version
         'Programming Language :: Python',
         'Programming Language :: Python :: 3',
-        'Programming Language :: Python :: 3.5',
         'Programming Language :: Python :: 3.6',
         'Programming Language :: Python :: 3.7',
     ],


Test patch:
diff --git a/.github/workflows/pytest-ci.yml b/.github/workflows/pytest-ci.yml
new file mode 100644
index 0000000..5d015a1
--- /dev/null
+++ b/.github/workflows/pytest-ci.yml
@@ -0,0 +1,33 @@
+name: Pytest
+
+on:
+  push:
+    branches:
+      - master
+      - latest
+  pull_request:
+    types: [opened, reopened, synchronize]
+
+jobs:
+  build:
+    runs-on: ubuntu-latest
+    strategy:
+      matrix:
+        python-version: ["3.6", "3.7"]
+
+    steps:
+      - uses: actions/checkout@v2
+      - name: Set up Python ${{ matrix.python-version }}
+        uses: actions/setup-python@v2
+        with:
+          python-version: ${{ matrix.python-version }}
+      - name: Display Python Version
+        run: python -c "import sys; print(sys.version)"
+      - name: Install dependencies
+        run: |
+          python -m pip install --upgrade pip
+          pip install pytest
+          pip install -r requirements.txt
+      - name: Test with pytest
+        run: |
+          pytest
diff --git a/tests/test_prep_excel.py b/tests/test_prep_excel.py
index 38fa3a6..28a69a8 100644
--- a/tests/test_prep_excel.py
+++ b/tests/test_prep_excel.py
@@ -64,5 +64,5 @@ def test__dataframe_table():
     result = pd.read_csv('tests/data/2013 DUGAAIR.csv',
                          index_col=0, parse_dates=True)
     assert_frame_equal(
-        result, dataframe
+        result, dataframe, check_like=True
     )