Mencari nilai perbedaan pada MultiIndex

Original Post: diskusi @teamalgoritma/community #170

Untuk melakukan perhitungan dengan multiindex index, bisa juga menggunakan .groupby(...).

Pada contoh kasus diatas, bisa menggunakan:

test['Visitor Growth'] = test.groupby('Weekday').diff()

atau selain menggunakan key, bisa juga menggunakan level indexnya level=, menjadi:

test['Visitor Growth'] = test.groupby(level=0).diff() # 0 = multiindex index pertama

Saya belum cek apakah konteksnya sama atau tidak, tapi saya bangkitkan dataframe yang menyerupai data aslinya:

import numpy as np
import pandas as pd

np.random.seed(1234)

index_date = pd.date_range('20100101', '20151231')
dummy = pd.DataFrame(np.random.randint(100, 3000, index_date.size), index=index_date, columns=['VALUE'])
# dummy['DATE'] = dummy.index.strftime('%d')
dummy['DAY'] = dummy.index.strftime('%A')
dummy['MONTH'] = dummy.index.strftime('%B')
dummy['YEAR'] = dummy.index.strftime('%Y')
test = dummy.groupby(['YEAR', 'MONTH', 'DAY']).sum()

test

image

dengan data tersebut, misalnya saya mau melihat VALUE_GROWTH setiap bulannya (level=1). Maka, perintah yang digunakan:

test.groupby(['YEAR', 'MONTH']).diff()
# bisa juga ditulis menggunakan level
# test.groupby(level=[0, 1]).diff()

image

untuk memastikan apakah nilainya benar, bisa dibandingkan langsung dengan .diff() seperti tahapan awal solusi dari @triarm

testcopy = test.copy()
testcopy['VALUE_GROWTH_MONTH'] = test.groupby(by=['YEAR', 'MONTH']).diff()
testcopy['DIFF'] = test.diff()
testcopy

image

Diperoleh hasil yang sama juga. Jadi, penggunaan .groupby(...).diff() bisa dijadikan alternatif.


Mungkin pertanyaan berikutnya, kenapa harus argumennya by=['YEAR', 'MONTH']? dan tidak by='MONTH' (ini pertanyaan yang muncul pas saya buat jawaban ini soalnya. 🤣) karena saya lihat sekilas hasilnya sama saja? seperti dilihat di gambar ini:

image

Rekomendasi saya pada saat melakukan ini, selalu evaluasi awal dan akhir dari dataframe, karena bisa saja bagian atasnya benar tapi bawahnya salah. Jika dilihat menggunakan tail akan muncul perbedaannya, dan hasilnya tidak sesuai ekspetasi kita. Coba dilihat menggunakan .tail(...). Hasilnya sebagai berikut:

image

disini setiap nilai awal index DAY nilainya tidak NaN terlepas hasil .diff()-nya sama.


Hal tersebut dikarenakan pada proses .groupby(...), jika yang digunakan hanya label tertentu (pada kasus ini yaitu MONTH saja), maka dia akan mengelompokkan keseluruhan data dengan label tersebut saja. Sehingga, index label lainnya (YEAR) akan diabaikan.

Contoh ilustrasinya jika digunakan hanya MONTH saja (level=1):

groupby_single

Di ilustrasi tersebut, terlihat pada B1 yang bagian dari A2, diurutkan sebelum B2. Maka dari itu, elemen awal setiap B, perintah .diff() akan mengacu pada nilai sebelumnya terlepas pemisah dari index A. Pada gambar tersebut nilai A2-B1-C1 merupakan hasil dari D7-D3, dimana nilai D3 berada di A1-B1-C3.

Dan jika kita menggunakan level secara bertahap yaitu ['YEAR', 'MONTH'] atau [0, 1], maka dapat diilustrasikan sebagai berikut:

groupby_multi

Pada gambar diatas, hasilnya sesuai dengan harapan kita, dimana nilai dikelompokkan per YEAR, baru dikelompokkan kembali per MONTH.

Untuk contoh kasus @RanggaGemilang, sebenarnya bisa langsung dengan mengetikkan by='Weekday' / level=0 dikarenakan itu index pertama dari multiindex-index. Sedangkan contoh kasus saya, nilai yang ingin direkap merupakan index kedua, sehingga pada saat perhitungan pengelompokkan, harus juga disertakan dengan index pertama.


Jadi, solusi ini lebih mengarahkan bagaimana mengelompokkan nilai menjadi Series terlebih dahulu dan baru menerapkan sebuah fungsi (contohnya: .diff()).

Semoga bermanfaat. :)