06/04/2021, 14:46

Hierarchical Indexing trong Pandas - Pandas

Từ đầu chương đến giờ, chúng ta đã tìm hiểu và sử dụng về Series và DataFrame khá nhiều và nó tỏ ra rất hữu ích trong việc lưu trữ cũng như thao tác dữ liệu. Thực tế thì như trong các bài trước đã nói, chúng ta hình dung Series như là mảng 1 chiều còn DataFrame là mảng 2 chiều. Tuy nhiên trong ...

Từ đầu chương đến giờ, chúng ta đã tìm hiểu và sử dụng về Series và DataFrame khá nhiều và nó tỏ ra rất hữu ích trong việc lưu trữ cũng như thao tác dữ liệu. Thực tế thì như trong các bài trước đã nói, chúng ta hình dung Series như là mảng 1 chiều còn DataFrame là mảng 2 chiều.

Tuy nhiên trong thực tế chúng ta không chỉ có sử dụng dữ liệu ở 1D hay 2D mà còn cần thao tác với những tập dữ liệu ở chiều cao hơn. Điều này được Pandas giải quyết bằng Hierarchical Indexing (Chỉ mục phân cấp - hay còn gọi là Multi-level indexing). Về bản chất, nó cho phép chúng ta lưu trữ và thao tác tuỳ ý với dữ liệu ở bất kì chiều nào trong Series (1D) hay DataFrame (2D).

Trong bài này, chúng ta sẽ tìm hiểu về cách tạo nên MultiIndex object cũng như các thao tác index, slicing, tính toán,... với nó.

1. Giới thiệu về MultiIndex

Giả sử chúng ta cần thống kê dữ liệu về các bệnh nhân mắc COVID-19 ở Việt Nam qua 2 tháng đầu năm 2019, với những gì ta đã học trong Pandas thì ta có thể sử dụng tuples cho index như sau:

In[2]
# Tuples đại diện cho Tỉnh / TP - tháng tương ứng
index = [('Ho Chi Minh', 1), ('Ho Chi Minh', 2), 
         ('Vinh Phuc', 1), ('Vinh Phuc', 2), 
         ('Thanh Hoa', 1), ('Thanh Hoa', 2)]

data = [2, 1, 2, 7, 1, 0]

vncovid = pd.Series(data, index=index)

print(vncovid)
Out[2]
(Ho Chi Minh, 1)    2
(Ho Chi Minh, 2)    1
(Vinh Phuc, 1)      2
(Vinh Phuc, 2)      7
(Thanh Hoa, 1)      1
(Thanh Hoa, 2)      0
dtype: int64

Giả sử ta muốn lấy số người bệnh ở TP.HCM (tháng 1 và 2) và Vĩnh Phúc (tháng 1) ta có thể slicing trực tiếp như sau:

In[3]
vncovid[('Ho Chi Minh', 1):('Vinh Phuc', 1)]
Out[3]
(Ho Chi Minh, 1)    2
(Ho Chi Minh, 2)    1
(Vinh Phuc, 1)      2
dtype: int64

Ta có thể thấy với cách này, việc lấy dữ liệu trong một khoảng khá là tiện dụng, nhưng chỉ tiện dụng tới đó. Giả sử ta muốn lấy dữ liệu của tất cả các Tỉnh / Thành phố trong tháng 2 thì sao? Khi đó ta bắt buộc phải sử dụng những cách khá là "xấu" như vòng lặp sau:

In[4]
vncovid[[i for i in vncovid.index if i[1] == 2]]
Out[4]
(Ho Chi Minh, 2)    1
(Vinh Phuc, 2)      7
(Thanh Hoa, 2)      0
dtype: int64

Ta vẫn có thể dùng vòng lặp, nhưng bản chất ở trong chương NumPy mình đã nhắc đến vectorized operation. Nếu như ta sử dụng các phương thức có sẵn trong Pandas / NumPy, tốc độ sẽ nhanh hơn nhiều so với dùng vòng lặp truyền thống, đặc biệt khi trên là tập dữ liệu COVID của toàn thế giới có thể lên hàng chục GB thì nếu dùng vòng lặp sẽ rất mất nhiều thời gian, cũng như viết khá nhiều code sẽ dễ dẫn tới bug, trong khi mục đích mà ta dùng Pandas chính là để thao tác với dữ liệu dễ dàng và ít phải code hơn.

Do vậy, Pandas cho ta một cách để tiếp cận và giải quyết vấn đề này một cách đơn giản đó chính là Hierarchical Indexing thông qua MultiIndex object. Từ tuples trên, ta có thể tạo MultiIndex bằng cách sau:

In[5]
ind = pd.MultiIndex.from_tuples(index, names=['Tỉnh/TP', 'Tháng'])

ind
Out[5]
MultiIndex([('Ho Chi Minh', 1),
            ('Ho Chi Minh', 2),
            (  'Vinh Phuc', 1),
            (  'Vinh Phuc', 2),
            (  'Thanh Hoa', 1),
            (  'Thanh Hoa', 2)],
           names=['Tỉnh/TP', 'Tháng'])

Và nếu reindex lại Series trên bằng MultiIndex mới, ta sẽ thấy được sự phân cấp của dữ liệu:

In[6]
vncovid = vncovid.reindex(ind)

vncovid
Out[6]
Tỉnh/TP      Tháng
Ho Chi Minh  1        2
             2        1
Vinh Phuc    1        2
             2        7
Thanh Hoa    1        1
             2        0
dtype: int64

Với ví dụ trên, 2 cột đầu tiên đại diện cho 2 loại index còn cột thứ 3 là dữ liệu tương ứng với các index đó. Một điều cần chú ý là có thể thấy ở cột index "Tỉnh/TP" bị khuyết dữ liệu ở một số hàng, thì những chỗ khuyết này có giá trị tương ứng với hàng ở trên, bạn có thể thấy cách cấu trúc này khá giống với cách mà chúng ta hay sử dụng Excel.

Và vì giờ có đến 2 cột index, với bài toán lấy dữ liệu ở tháng 2 ở trên ta có thể làm đơn giản như sau:

In[7]
vncovid[:, 2]
Out[7]
Tỉnh/TP
Ho Chi Minh    1
Vinh Phuc      7
Thanh Hoa      0
dtype: int64

Như vậy, ta có thể thấy rằng việc sử dụng MultiIndex thuận tiện hơi rất nhiều so với việc dùng tuples để lưu các index. Bạn cũng có thể thấy kiểu cấu trúc trên khá giống với DataFrame, và Pandas có hỗ trợ cả phương thức chuyển từ MultiIndex Series sang DataFrame như sau:

In[8]
vncovid_df = vncovid.unstack()

print(vncovid_df)
Out[8]
Tháng        1  2
Tỉnh/TP          
Ho Chi Minh  2  1
Thanh Hoa    1  0
Vinh Phuc    2  7

Và ngược lại:

In[9]
vncovid_df.stack()
Out[9]
Tỉnh/TP      Tháng
Ho Chi Minh  1        2
             2        1
Thanh Hoa    1        1
             2        0
Vinh Phuc    1        2
             2        7
dtype: int64

Đến đây chắc hẳn bạn sẽ thắc mắc một điều rằng: ta hoàn toàn có thể giải quyết bài toán trên bằng DataFrame, việc gì phải dùng Series rồi thêm MultiIndex cho dài dòng và phức tạp? Câu trả lời là giống như việc mà ta sử dụng MultiIndex cho Series (1D) để biểu diễn dữ liệu 2D, thì chúng ta hoàn toàn có thể lưu trữ dữ liệu 3D và hơn nữa (4D, 5D,...) trên DataFrame (2D). Cứ thêm 1 cấp index thì ta lại thêm 1 chiều dữ liệu. Giả sử ta cần biểu diễn thêm cột là số người dưới 30 tuổi mắc COVID:

In[9]
vncovid_df = pd.DataFrame({'Số ca nhiễm': vncovid, 'Dưới 30 tuổi': [1, 0, 2, 4, 1, 0]})

vncovid_df
Out[9]
                   Số ca nhiễm  Dưới 30 tuổi
Tỉnh/TP     Tháng                           
Ho Chi Minh 1                2             1
            2                1             0
Vinh Phuc   1                2             2
            2                7             4
Thanh Hoa   1                1             1
            2                0             0

Ta dễ dàng biểu diễn được tỉ lệ người dưới 30 tuổi nhiễm bệnh tại từng tỉnh / TP qua từng tháng bằng các phép toán đã được học ở bài trước như sau:

In[10]
vncovid_under30 = vncovid_df['Dưới 30 tuổi'].div(vncovid_df['Số ca nhiễm']).fillna(0)

vncovid_under30.unstack()
Out[10]
Tháng          1         2
Tỉnh/TP                   
Ho Chi Minh  0.5  0.000000
Thanh Hoa    1.0  0.000000
Vinh Phuc    1.0  0.571429

Vậy có thể thấy rằng với MultiIndex, ta có thể tính toán và xử lý các vấn đề khá phức tạp với những dữ liệu nhiều chiều một cách đơn giản và nhanh chóng.

2. Các phương thức tạo MultiIndex

Tạo MultiIndex cho hàng

Cách đơn giản nhất để tạo một Series hoặc DataFrame chứa đa chỉ mục là truyền vào nhiều hơn 2 mảng index trong tham số khi khởi tạo, ví dụ:

In[11]
df = pd.DataFrame(np.random.rand(6, 2), index=[['a', 'a', 'b', 'b', 'b', 'b'], [1, 2, 1, 2, 3, 4]], columns=['X', 'Y'])

df
Out[11]
            X         Y
a 1  0.996862  0.311057
  2  0.793059  0.048556
b 1  0.183056  0.543373
  2  0.482581  0.890674
  3  0.747200  0.221476
  4  0.781251  0.805360

Ngoài ra, MultiIndex còn có thể được tạo từ mảng bằng các phương thức sau:

from_arrays (từ mảng)

In[12]
index = pd.MultiIndex.from_arrays([['a', 'a', 'b', 'b'], [1, 2, 1, 2]], names=['Index A', 'Index B'])

pd.Series([1, 2, 3, 4], index)
Out[12]
Index A  Index B
a        1          1
         2          2
b        1          3
         2          4
dtype: int64

from_tuples (từ tuples)

In[13]
index = pd.MultiIndex.from_tuples([('a', 1), ('a', 2),('a', 3),('b', 1)], names=['Index A', 'Index B'])

pd.Series([1, 2, 3, 4], index)
Out[12]
Index A  Index B
a        1          1
         2          2
         3          3
b        1          4
dtype: int64

from_product (từ tích Descartes)

Tích Descartes của 2 tập hợp A và B là một tập hợp chứa tất cả các phần tử dạng (a, b) với a là một phần tử của A và b là một phần tử của B, hay nói đơn giản hơn là ta ghép từng phần tử của tập này với từng phần tử của tập kia, ví dụ:

A = {1,2} và B = {p,q,r} thì: A×B = {(1,p),(1,q),(1,r),(2,p),(2,q),(2,r)}

Để tạo MultiIndex trong Pandas từ tích Descartes ta làm như sau:

In[14]
index = pd.MultiIndex.from_product([['bar', 'baz'], ['one', 'two']], names=['Index A', 'Index B'])

pd.Series([1, 2, 3, 4], index)
Out[14]
Index A  Index B
bar      one        1
         two        2
baz      one        3
         two        4
dtype: int64

Tạo MultiIndex cho cột

Trong nhiều trường hợp phức tạp hơn, không chỉ có hàng mà cột cũng cần có đa chỉ mục. Để tạo đa chỉ mục cho cột thì giống như hàng tuy nhiên ta sẽ truyền vào tham số columns thay vì index, ta có một ví dụ:

In[15]
index = pd.MultiIndex.from_product([[2018, 2019, 2020], [1, 2, 3]], names=['Năm', 'Lần thi'])
columns = pd.MultiIndex.from_product([['Minh', 'Lan', 'Nga'], ['Toán', 'Lý', 'Hoá']], names=['Thí sinh', 'Môn'])

# Tạo dữ liệu mẫu
data = np.random.randint(5, 10, (9, 9))
score = pd.DataFrame(data, index=index, columns=columns)
score
Out[15]
Thí sinh     Minh         Lan         Nga       
Môn          Toán Lý Hoá Toán Lý Hoá Toán Lý Hoá
Năm  Lần thi                                    
2018 1          9  7   5    5  6   8    6  5   7
     2          7  6   6    9  5   5    9  6   8
     3          7  9   7    5  5   8    9  9   5
2019 1          5  6   6    5  9   9    8  6   9
     2          7  9   8    7  9   9    6  5   8
     3          6  8   5    7  8   7    5  8   9
2020 1          9  7   5    8  9   9    9  6   7
     2          6  7   5    6  6   6    9  5   8
     3          8  6   7    8  9   7    5  6   6

Trên là một ví dụ mẫu về dữ liệu điểm thi của 3 bạn học sinh qua từng năm. Về cơ bản, dữ liệu ở trên là dữ liệu 4 chiều: năm, lần thi, thí sinh và môn học. Với việc cấu trúc như trên, để lấy dữ liệu của 1 thí sinh thì ta chỉ cần index tên thí sinh đó như sau:

In[16]
score['Minh']
Out[16]
Môn           Toán  Lý  Hoá
Năm  Lần thi               
2018 1           9   7    5
     2           7   6    6
     3           7   9    7
2019 1           5   6    6
     2           7   9    8
     3           6   8    5
2020 1           9   7    5
     2           6   7    5
     3           8   6    7

Ta có thể thấy việc truy cập và lấy dữ liệu cực kỳ thuận tiện và logic với đa chỉ mục, đặc biệt nếu như dữ liệu với số chiều càng nhiều, càng phức tạp thì việc sử dụng đa chỉ mục lên cả hàng và cột sẽ khiến cho ta dễ xử lý hơn rất nhiều.

3. Indexing và Slicing trong MultiIndex

Indexing và Slicing trong Pandas được thiết kế khá trực quan, ta sẽ tìm hiểu việc thao tác trên Series và DataFrame để cho các bạn có thể dễ hình dung với từng kiểu object khác nhau.

Với Series

Xét lại ví dụ ở đầu bài với Series chứa dữ liệu về các bệnh nhân mắc COVID-19 ở Việt Nam qua 2 tháng đầu năm 2019:

In[17]
vncovid
Out[17]
Tỉnh/TP      Tháng
Ho Chi Minh  1        2
             2        1
Vinh Phuc    1        2
             2        7
Thanh Hoa    1        1
             2        0
dtype: int64

Ta có thể truy cập từng phần tử một bằng cách indexing từng chỉ mục tương ứng:

In[18]
print("Số ca nhiễm trong tháng 2 tại TP.HCM: ", vncovid['Ho Chi Minh', 2])
Out[18]
Số ca nhiễm trong tháng 2 tại TP.HCM:  1

Hoặc ta có thể indexing vào chỉ 1 cấp của chỉ mục, khi đó trả về sẽ là 1 Series với các giá trị tương ứng với chỉ mục cấp thấp hơn:

In[19]
print("Số ca nhiễm tại Vĩnh Phúc: 
", vncovid['Vinh Phuc'])
Out[18]
Số ca nhiễm tại Vĩnh Phúc: 
Tháng
1    2
2    7
dtype: int64

Slicing hoạt động tương tự như indexing, chẳng hạn:

In[19]
print("Số ca nhiễm tại các tỉnh trong tháng 2: 
", vncovid.loc[:, 2])
Out[19]
Tỉnh/TP
Ho Chi Minh    1
Vinh Phuc      7
Thanh Hoa      0
dtype: int64

Và với Masks cũng như Fancy Indexing:

In[21]
# Masks
print("Tỉnh / Thành phố và tháng tương ứng có số ca nhiễm > 5: 
", vncovid[vncovid > 5])

# Fancy Indexing
print("
Số ca nhiễm tại TP.HCM và Thanh Hoá: 
", vncovid[['Ho Chi Minh', 'Thanh Hoa']])
Out[21]
Tỉnh / Thành phố và tháng tương ứng có số ca nhiễm > 5: 
Tỉnh/TP    Tháng
Vinh Phuc  2        7
dtype: int64

Số ca nhiễm tại TP.HCM và Thanh Hoá: 
Tỉnh/TP      Tháng
Ho Chi Minh  1        2
             2        1
Thanh Hoa    1        1
             2        0
dtype: int64

Với DataFrame

DataFrame cũng khá giống Series trong indexing và slicing với đa chỉ mục. Xét lại ví dụ về điểm số ở trên:

In[22]
score
Out[22]
Thí sinh     Minh         Lan         Nga       
Môn          Toán Lý Hoá Toán Lý Hoá Toán Lý Hoá
Năm  Lần thi                                    
2018 1          9  7   5    5  6   8    6  5   7
     2          7  6   6    9  5   5    9  6   8
     3          7  9   7    5  5   8    9  9   5
2019 1          5  6   6    5  9   9    8  6   9
     2          7  9   8    7  9   9    6  5   8
     3          6  8   5    7  8   7    5  8   9
2020 1          9  7   5    8  9   9    9  6   7
     2          6  7   5    6  6   6    9  5   8
     3          8  6   7    8  9   7    5  6   6

Mình sẽ ví dụ từng thao tác cụ thể dưới đây:

In[23]
print("Điểm Toán của Minh: 
" , score['Minh', 'Toán'])

print("
Điểm Hoá của Nga và Lý của Lan: 
", score.loc[:, [('Nga', 'Hoá'), ('Lan', 'Lý')]])

print("
Điểm Toán của Lan trong năm 2019: 
" , score.loc[2019, ('Lan', 'Toán')])

print("
Điểm Toán của Minh và Lý của Lan trong năm 2018 và 2020: 
", score.loc[[2018, 2020], [('Nga', 'Hoá'), ('Lan', 'Lý')]])

print("
Các lần thi Hoá của Nga có điểm > 7: 
", score['Nga', 'Hoá'][score['Nga', 'Hoá'] > 7])
Out[23]
Điểm Toán của Minh: 
 Năm   Lần thi
2018  1          9
      2          7
      3          7
2019  1          5
      2          7
      3          6
2020  1          9
      2          6
      3          8
Name: (Minh, Toán), dtype: int32

Điểm Hoá của Nga và Lý của Lan: 
 Thí sinh     Nga Lan
Môn          Hoá  Lý
Năm  Lần thi        
2018 1         7   6
     2         8   5
     3         5   5
2019 1         9   9
     2         8   9
     3         9   8
2020 1         7   9
     2         8   6
     3         6   9

Điểm Toán của Lan trong năm 2019: 
 Lần thi
1    5
2    7
3    7
Name: (Lan, Toán), dtype: int32

Điểm Toán của Minh và Lý của Lan trong năm 2018 và 2020: 
 Thí sinh     Nga Lan
Môn          Hoá  Lý
Năm  Lần thi        
2018 1         7   6
     2         8   5
     3         5   5
2020 1         7   9
     2         8   6
     3         6   9

Các lần thi Hoá của Nga có điểm > 7: 
 Năm   Lần thi
2018  2          8
2019  1          9
      2          8
      3          9
2020  2          8
Name: (Nga, Hoá), dtype: int32

4. Sắp xếp lại chỉ mục trong MultiIndex

Sắp xếp lại chỉ mục là một điều khá quan trọng khi thao tác với dữ liệu. Chúng ta đã tìm hiểu về 2 phương thức là stack unstack, tuy nhiên vẫn còn một số phương thức hữu ích khác sẽ được mình giới thiệu dưới đây.

Sorted & unsorted indices

Trong MultiIndex, nếu như chỉ mục không được sắp xếp thì khi bạn thực hiện slicing từng phần để lấy một số phần tử trong Series hay DataFrame thì sẽ xảy ra lỗi, ta có một ví dụ nhỏ với chỉ mục không được sắp xếp theo từ vựng sau:

In[24]
index = pd.MultiIndex.from_product([['a', 'c', 'b'], [1, 2, 3]], names=['Index A', 'Index B'])
data = pd.Series(np.random.randint(0, 100, (9,)), index=index)
data
Out[24]
Index A  Index B
a        1          46
         2          15
         3          40
c        1          22
         2          39
         3           1
b        1          65
         2          87
         3          10
dtype: int32

Nếu như ta thử slicing lấy dữ liệu theo Index A lập tức sẽ hiển thị lỗi sau:

In[25]
data['a':'b']
Out[25]
...
UnsortedIndexError: 'Key length (1) was greater than MultiIndex lexsort depth (0)'

Vì nhiều lý do khác nhau, Pandas bắt buộc index phải được sắp xếp theo thứ tự mới có thể lấy được dữ liệu bằng slicing. Bên cạnh đó, thư viện này cũng hỗ trợ phương thức giúp ta nhanh chóng sắp xếp index là sort_index:

In[26]
data = data.sort_index()

data['a':'b']
Out[26]
Index A  Index B
a        1          46
         2          15
         3          40
b        1          65
         2          87
         3          10
dtype: int32

Stacking & unstacking indices

Như ở ví dụ đầu tiên, ta có thể sử dụng hai phương thức là stack unstack để chuyển các Series / DataFrame nhiều chiều về dạng 2 chiều đơn giản, ngoài ra 2 phương thức này còn hỗ trợ chọn cấp chỉ mục bằng tham số level như sau:

In[27]
print("Unstack level 0: 
" , vncovid.unstack(level=0))
print("
Unstack level 1: 
" , vncovid.unstack(level=1))
Out[27]
Unstack level 0: 
Tỉnh/TP  Ho Chi Minh  Thanh Hoa  Vinh Phuc
Tháng                                     
1                  2          1          2
2                  1          0          7

Unstack level 1: 
Tháng        1  2
Tỉnh/TP          
Ho Chi Minh  2  1
Thanh Hoa    1  0
Vinh Phuc    2  7

Phương thức stack có tác dụng ngược lại với unstack, ta có thể dùng phương thức này khi muốn đảo ngược và lấy giá trị gốc ban đầu:

In[28]
print("vncovid: 
", vncovid)

print("
 vncovid unstack => stack: 
", vncovid.unstack(level=1).stack())
Out[28]
vncovid: 
Tỉnh/TP      Tháng
Ho Chi Minh  1        2
             2        1
Vinh Phuc    1        2
             2        7
Thanh Hoa    1        1
             2        0
dtype: int64

vncovid unstack => stack: 
Tỉnh/TP      Tháng
Ho Chi Minh  1        2
             2        1
Thanh Hoa    1        1
             2        0
Vinh Phuc    1        2
             2        7
dtype: int64

Index setting & resetting

Một trong những cách sắp xếp lại đa chỉ mục đó chính là phương thức reset_index, phương thức này sẽ biến xoá đa chỉ mục và biến chúng thành các cột tương ứng:

In[29]
print("vncovid DataFrame: 
", vncovid_df)

print("
vncovid DataFrame sau khi reset index: 
", vncovid_df.reset_index())
Out[29]
vncovid DataFrame: 
                    Số ca nhiễm  Dưới 30 tuổi
Tỉnh/TP     Tháng                           
Ho Chi Minh 1                2             1
            2                1             0
Vinh Phuc   1                2             2
            2                7             4
Thanh Hoa   1                1             1
            2                0             0

vncovid DataFrame sau khi reset index: 
        Tỉnh/TP  Tháng  Số ca nhiễm  Dưới 30 tuổi
0  Ho Chi Minh      1            2             1
1  Ho Chi Minh      2            1             0
2    Vinh Phuc      1            2             2
3    Vinh Phuc      2            7             4
4    Thanh Hoa      1            1             1
5    Thanh Hoa      2            0             0

Một điều thú vị là khi làm việc với dữ liệu trong thực tế thì trông nó khá là giống DataFrame đã được reset index trên. Khi đó để đặt đa chỉ mục cho DataFrame này ta sẽ dùng phương thức set_index:

In[30]
vncovid_df_reset = vncovid_df.reset_index()

print("
vncovid DataFrame: 
", vncovid_df_reset.set_index(['Tỉnh/TP', 'Tháng']))
Out[30]
vncovid DataFrame: 
                    Số ca nhiễm  Dưới 30 tuổi
Tỉnh/TP     Tháng                           
Ho Chi Minh 1                2             1
            2                1             0
Vinh Phuc   1                2             2
            2                7             4
Thanh Hoa   1                1             1
            2                0             0

5. Thực hiện tính toán với MultiIndex

Như đã nói ở các bài trước, Pandas hầu như đều hỗ trợ các phương thức tính toán như sum, max, min,.... Với Series / DataFrame mang đa chỉ mục, chúng ta hoàn toàn có thể sử dụng các phương thức này với tham số level tương ứng với cấp mà chúng ta muốn thao tác. Quay lại với DataFrame score mà ta đã tạo ở ví dụ trước:

In[31]
score
Out[31]
Thí sinh     Minh         Lan         Nga       
Môn          Toán Lý Hoá Toán Lý Hoá Toán Lý Hoá
Năm  Lần thi                                    
2018 1          9  7   5    5  6   8    6  5   7
     2          7  6   6    9  5   5    9  6   8
     3          7  9   7    5  5   8    9  9   5
2019 1          5  6   6    5  9   9    8  6   9
     2          7  9   8    7  9   9    6  5   8
     3          6  8   5    7  8   7    5  8   9
2020 1          9  7   5    8  9   9    9  6   7
     2          6  7   5    6  6   6    9  5   8
     3          8  6   7    8  9   7    5  6   6

Giả sử chúng ta cần tính điểm trung bình của từng thí sinh theo từng môn học trong các năm:

In[32]
score.mean(level='Năm')
Out[32]
Thí sinh      Minh                           Lan                       # Xuống dòng
Môn           Toán        Lý       Hoá      Toán        Lý       Hoá   
Năm                                                                    
2018      7.666667  7.333333  6.000000  6.333333  5.333333  7.000000   
2019      6.000000  7.666667  6.333333  6.333333  8.666667  8.333333   
2020      7.666667  6.666667  5.666667  7.333333  8.000000  7.333333   

Thí sinh       Nga                      
Môn           Toán        Lý       Hoá  
Năm                                     
2018      8.000000  6.666667  6.666667  
2019      6.333333  6.333333  8.666667  
2020      7.666667  5.666667  7.000000  

Ta có thể thêm tham số axis=1 nếu như muốn tính toán theo cột thay vì hàng, giả sử ta cần tính trung bình điểm các môn qua từng lần thi:

In[33]
score.mean(level='Môn', axis=1)
Out[33]
Môn               Toán        Lý       Hoá
Năm  Lần thi                              
2018 1        6.666667  6.000000  6.666667
     2        8.333333  5.666667  6.333333
     3        7.000000  7.666667  6.666667
2019 1        6.000000  7.000000  8.000000
     2        6.666667  7.666667  8.333333
     3        6.000000  8.000000  7.000000
2020 1        8.666667  7.333333  7.000000
     2        7.000000  6.000000  6.333333
     3        7.000000  7.000000  6.666667

6. Tổng kết

Hierarchical Indexing là một tính năng vô cùng quan trọng trong Pandas, nó giúp ta xử lý cũng như trực quan hoá dữ liệu nhiều chiều một cách đơn giản và tường minh cũng như giảm thiểu lượng code phải viết, giúp ta hạn chế được bug. Trong bài tiếp theo chúng ta sẽ tìm hiểu về Kết hợp các tập dữ liệu trong Pandas, hẹn gặp các bạn ở bài tiếp nhé.

0