Reindex untuk MultiIndex di pandas

Original Post: diskusi @teamalgoritma/community #169

Untuk kasus ini, saya menangkapnya bahwa urutan index Quarter ingin diurutkan secara terbalik (terbaru diatas). Solusi lain adalah dengan membuat objek index pd.MultiIndex yang menyerupai dari original index.

Kalau lihat polanya bahwa ada dua label di multiindex yaitu Country dan Quarter. Dari sampel data diatas bisa disajikan dalam bentuk list.

COUNTRY = ['EIRE', 'France', 'Australia']
QUARTER = ['2010Q4', '2011Q1', '2011Q2']

Untuk membalikkan elemen di QUARTER bisa menggunakan metode slicing [::-1].

quarter_reverse = QUARTER[::-1]

setelah itu bisa membuat objek pd.MultiIndex dengan kedua list tersebut.

new_index = pd.MultiIndex.from_product([COUNTRY, quarter_reverse])
>>> MultiIndex([(     'EIRE', '2011Q2'),
                (     'EIRE', '2011Q1'),
                (     'EIRE', '2010Q4'),
                (   'France', '2011Q2'),
                (   'France', '2011Q1'),
                (   'France', '2010Q4'),
                ('Australia', '2011Q2'),
                ('Australia', '2011Q1'),
                ('Australia', '2010Q4')],
            )

Dari sini tinggal memanggil dataframe invoice_topq.loc[new_index] dan akan muncul dengan urutan yang baru.

Berikut kode solusi yang serupa tanpa hardcode nilai Country atau Quarter:

country = invoice_topq.index.get_level_values(0).unique() # index multiindex pertama (0) / country
quarter = invoice_topq.index.levels[1][::-1] # index multiindex kedua / quarter (dan dibalikkan)
new_index = pd.MultiIndex.from_product([country, quarter])
invoice_topq.loc[new_index]

Alasan menggunakan .index.get_level_values(...).unique() untuk mempertahankan posisi urutan index. Jika urutan bukanlah hal yang penting, maka untuk memperoleh list cukup dengan .index.levels[...].


Berikut contoh kasus lainnya (sama dengan jawaban saya di #170). Dataframe test:

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

Saya ingin mengurutkan dengan bulan January-December dan hari Monday-Sunday.

import calendar

level0 = test.index.levels[0]
level1 = list(calendar.month_name)[-12:] # first element is empty
level2 = list(calendar.day_name)
new_index = pd.MultiIndex.from_product(
    [level0, level1, level2], names=['YEAR', 'MONTH', 'DAY']
)
new_index
>>> MultiIndex([('2010',  'January',    'Monday'),
                ('2010',  'January',   'Tuesday'),
                ('2010',  'January', 'Wednesday'),
                ('2010',  'January',  'Thursday'),
                ('2010',  'January',    'Friday'),
                ('2010',  'January',  'Saturday'),
                ('2010',  'January',    'Sunday'),
                ('2010', 'February',    'Monday'),
                ('2010', 'February',   'Tuesday'),
                ('2010', 'February', 'Wednesday'),
                ...
                ('2015', 'November',    'Friday'),
                ('2015', 'November',  'Saturday'),
                ('2015', 'November',    'Sunday'),
                ('2015', 'December',    'Monday'),
                ('2015', 'December',   'Tuesday'),
                ('2015', 'December', 'Wednesday'),
                ('2015', 'December',  'Thursday'),
                ('2015', 'December',    'Friday'),
                ('2015', 'December',  'Saturday'),
                ('2015', 'December',    'Sunday')],
            names=['YEAR', 'MONTH', 'DAY'], length=504)

Dari sini tinggal menggunakan .loc[new_index].

test.loc[new_index]

image

Untuk mengecek nilainya benar atau tidak, bisa menggunakan slice:

test.loc[pd.IndexSlice[:, 'January', 'Monday']]

image


Referensi: