DataFrame 인덱스 조작

2020년 10월 06일
16

DataFrame 인덱스 설정 및 제거

DataFrame에 인덱스로 들어가 있어야 할 데이터가 일반 데이터 열에 들어가 있거나 반대로 일반 데이터 열이어야 할 것이 인덱스로 되어 있을 수 있다. 이 때는 set_index 명령이나 reset_index 명령으로 인덱스와 일반 데이터 열을 교환할 수 있다.

  • set_index : 기존의 행 인덱스를 제거하고 데이터 열 중 하나를 인덱스로 설정
  • reset_index : 기존의 행 인덱스를 제거하고 인덱스를 데이터 열로 추가
import numpy as np
np.vstack([list('ABCDE'), np.round(np.random.rand(3,5), 2)])

array([['A', 'B', 'C', 'D', 'E'],
['0.67', '0.21', '0.13', '0.32', '0.36'],
['0.57', '0.44', '0.99', '0.1', '0.21'],
['0.16', '0.65', '0.25', '0.47', '0.24']], dtype='U32')

import pandas as pd
np.random.seed(0)
df1 = pd.DataFrame(
    np.vstack([list('ABCDE'), np.round(np.random.rand(3,5), 2)]).T,
    columns=["C1","C2","C3","C4"])
df1
indexC1C2C3C4
0A0.550.650.79
1B0.720.440.53
2C0.60.890.57
3D0.540.960.93
4E0.420.380.07

set_index 명령으로 C1열을 인덱스로 설정할 수 있다. 이때 기존 인덱스는 없어진다.

df2 = df1.set_index("C1")
df2
C1C2C3C4
A0.550.650.79
B0.720.440.53
C0.60.890.57
D0.540.960.93
E0.420.380.07

마찬가지로 C2열을 인덱스로 지정하면 기존의 인덱스는 사라진다.

df2.set_index("C2")
C2C3C4
0.550.650.79
0.720.440.53
0.60.890.57
0.540.960.93
0.420.380.07

reset_index 명령으로 인덱스를 보통의 자료형으로 바꿀 수도 있다. 이 때 인덱스 열은 자료형의 가장 선두로 삽입된다. DataFrame의 인덱스는 정수로 된 디폴트 인덱스로 바뀌게 된다.

df2.reset_index()
indexC1C2C3C4
0A0.550.650.79
1B0.720.440.53
2C0.60.890.57
3D0.540.960.93
4E0.420.380.07

reset_index 명령 사용시에 drop=True로 설정하면 인덱스 열을 보통의 자료열로 올리는 것이 아니라 그냥 버리게 된다.

df2.reset_index(drop=True)
indexC2C3C4
00.550.650.79
10.720.440.53
20.60.890.57
30.540.960.93
40.420.380.07

다중 인덱스

행이나 열에 여러 계층을 가지는 인덱스 즉, **다중 인덱스(multi-index)**를 설정할 수도 있다. DataFrame을 생성할 때 columns인수에 다음 예제처럼 리스트의 리스트(행렬) 형태로 인덱스를 넣으면 다중 열 인덱스를 가지게 된다.

np.random.seed(0)
df3 = pd.DataFrame(
    np.round(np.random.randn(5,4), 2),
    columns=[["A","A","B","B"],
            ["C1","C2","C1","C2"]]
    )
df3
indexA_C1A_C2B_C1B_C2
01.760.400.982.24
11.87-0.980.95-0.15
2-0.100.410.141.45
30.760.120.440.33
41.49-0.210.31-0.85

다중 인덱스는 이름을 지정하면 더 편리하게 사용할 수 있다. 열 인덱스들의 이름 지정은 columns객체의 names속성에 리스트를 넣어서 지정한다.

df3.columns.names = ["Cidx1", "Cidx2"]
df3
Cidx1A_C1A_C2B_C1B_C2
01.760.400.982.24
11.87-0.980.95-0.15
2-0.100.410.141.45
30.760.120.440.33
41.49-0.210.31-0.85

마찬가지로 DataFrame을 생성할 때 index 인수에 리스트의 리스트(행렬) 형태로 인덱스를 넣으면 다중 (행) 인덱스를 가진다. 행 인덱스들의 이름 지정은 index 객체의 names 속성에 리스트를 넣어서 지정한다.

np.random.seed(0)
df4 = pd.DataFrame(np.round(np.random.randn(6, 4), 2),
                   columns=[["A", "A", "B", "B"],
                            ["C", "D", "C", "D"]],
                   index=[["M", "M", "M", "F", "F", "F"],
                          ["id_" + str(i + 1) for i in range(3)] * 2])
df4.columns.names = ["Cidx1", "Cidx2"]
df4.index.names = ["Ridx1", "Ridx2"]
df4
Ridx1Ridx2A_CA_DB_CB_D
Mid_11.760.400.982.24
Mid_21.87-0.980.95-0.15
Mid_3-0.100.410.141.45
Fid_10.760.120.440.33
Fid_21.49-0.210.31-0.85
Fid_3-2.550.650.86-0.74

행 인덱스와 열 인덱스 교환

stack 명령이나 unstack 명령을 쓰면 열 인덱스를 행 인덱스로 바꾸거나 반대로 행 인덱스를 열 인덱스로 바꿀 수 있다.

  • stack() : 열 인덱스 -> 행 인덱스로 변환
  • unstack() : 행 인덱스 -> 열 인덱스로 변환

stack 명령을 실행하면 열 인덱스가 반시계 방향으로 90도 회전한 것과 비슷한 모양이 된다. 마찬가지로 unstack 명령을 실행하면 행 인덱스가 시계 방향으로 90도 회전한 것과 비슷하다. 인덱스를 지정할 때는 문자열 이름과 순서를 표시하는 숫자 인덱스를 모두 사용할 수 있다.

df4.stack("Cidx1")
Ridx1Ridx2Cidx1CD
Mid_1A1.760.40
Mid_1B0.982.24
Mid_2A1.87-0.98
Mid_2B0.95-0.15
Mid_3A-0.100.41
Mid_3B0.141.45
Fid_1A0.760.12
Fid_1B0.440.33
Fid_2A1.49-0.21
Fid_2B0.31-0.85
Fid_3A-2.550.65
Fid_3B0.86-0.74
df4.stack(1)
Ridx1Ridx2Cidx2AB
Mid_1C1.760.98
Mid_1D0.402.24
Mid_2C1.870.95
Mid_2D-0.98-0.15
Mid_3C-0.100.14
Mid_3D0.411.45
Fid_1C0.760.44
Fid_1D0.120.33
Fid_2C1.490.31
Fid_2D-0.21-0.85
Fid_3C-2.550.86
Fid_3D0.65-0.74
df4.unstack("Ridx2")
Ridx1A_C_id_1A_C_id_2A_C_id_3A_D_id_1A_D_id_2A_D_id_3B_C_id_1B_C_id_2B_C_id_3B_D_id_1B_D_id_2B_D_id_3
F0.761.49-2.550.12-0.210.650.440.310.860.33-0.85-0.74
M1.761.87-0.100.40-0.980.410.980.950.142.24-0.151.45
df4.unstack(0)
Ridx2A_C_FA_C_MA_D_FA_D_MB_C_FB_C_MB_D_FB_D_M
id_10.761.760.120.400.440.980.332.24
id_21.491.87-0.21-0.980.310.95-0.85-0.15
id_3-2.55-0.100.650.410.860.14-0.741.45

다중 인덱스가 있는 경우 인덱싱

DataFrame이 다중 인덱스를 가지는 경우에는 인덱스가 하나의 라벨이나 숫자가 아니라 ()로 둘러싸인 튜플이 되어야 한다. 예를 들어 앞에서 만든 df3 DataFrame의 경우 다음과 같이 인덱싱할 수 있다.

df3
indexA_C1A_C2B_C1B_C2
01.760.400.982.24
11.87-0.980.95-0.15
2-0.100.410.141.45
30.760.120.440.33
41.49-0.210.31-0.85
df3[("B", "C1")]

0 0.98
1 0.95
2 0.14
3 0.44
4 0.31
Name: (B, C1), dtype: float64

df3.loc[0, ("B", "C1")]

0.98

df3.loc[0, ("B", "C1")] = 100
df3
indexA_C1A_C2B_C1B_C2
01.760.40100.002.24
11.87-0.980.95-0.15
2-0.100.410.141.45
30.760.120.440.33
41.49-0.210.31-0.85

만약 하나의 레벨 값만 넣으면 다중 인덱스 중에서 가장 상위의 값을 지정한 것으로 본다.

df3["A"]
Cidx2C1C2
01.760.40
11.87-0.98
2-0.100.41
30.760.12
41.49-0.21

df4 DataFrame은 다음과 같이 인덱싱할 수 있다.

df4
Ridx1Ridx2A_CA_DB_CB_D
Mid_11.760.400.982.24
Mid_21.87-0.980.95-0.15
Mid_3-0.100.410.141.45
Fid_10.760.120.440.33
Fid_21.49-0.210.31-0.85
Fid_3-2.550.650.86-0.74
df4.loc[:,("A","C")]

Ridx1 Ridx2
M id_1 1.76
id_2 1.87
id_3 -0.10
F id_1 0.76
id_2 1.49
id_3 -2.55
Name: (A, C), dtype: float64

df4.loc[("M", "id_1"), :]

Cidx1 Cidx2
A C 1.76
D 0.40
B C 0.98
D 2.24
Name: (M, id_1), dtype: float64

df4.loc[("All", "All"), :] = df4.sum()
df4
Cidx1AB
Cidx2CD
-------------------------------
Ridx1Ridx2
-------------------------------
Mid_11.760.40
id_21.87-0.98
id_3-0.100.41
Fid_10.760.12
id_21.49-0.21
id_3-2.550.65
AllAll6.460.78

다중 인덱스의 인덱스 순서 교환

다중 인덱스의 인덱스 순서를 바꾸고 싶으면 swaplevel 명령을 사용한다.

  • `swaplevel(i, j, axis)

ij는 교환하고자 하는 인덱스 라벨(혹은 인덱스 번호)이고 axis는 0일 때 행 인덱스, 1일때는 열 인덱스를 뜻한다.(기본값은 0)

df5 = df4.swaplevel("Ridx1", "Ridx2")
df5
Cidx1AB
Cidx2CD
-------------------------------
Ridx2Ridx1
-------------------------------
id_1M1.760.40
id_2M1.87-0.98
id_3M-0.100.41
id_1F0.760.12
id_2F1.49-0.21
id_3F-2.550.65
AllAll6.460.78
df6 = df4.swaplevel("Cidx1", "Cidx2", 1)
df6
Cidx2CDCD
Cidx1AABB
-----------------------------------------------------
Ridx1Ridx2
-----------------------------------------------------
id_1M1.760.400.982.24
id_2M1.87-0.980.95-0.15
id_3M-0.100.410.141.45
id_1F0.760.120.440.33
id_2F1.49-0.210.31-0.85
id_3F-2.550.650.86-0.74
AllAll6.460.787.364.56

다중 인덱스가 있는 경우의 정렬

다중 인덱스가 있는 데이터프레임을 sort_index로 정렬할 때는 level 인수를 사용하여 어떤 인덱스를 기준으로 정렬하는지 알려주어야 한다.

df5.sort_index(level=0)
Cidx2CDCD
Cidx1AABB
-----------------------------------------------------
Ridx2Ridx1
-----------------------------------------------------
AllAll6.460.787.364.56
id_1F0.760.120.440.33
id_1M1.760.400.982.24
id_2F1.49-0.210.31-0.85
id_2M1.87-0.980.95-0.15
id_3F-2.550.650.86-0.74
id_3M-0.100.410.141.45
df6.sort_index(axis=1, level=0)
Cidx2ABAB
Cidx1CDCD
-----------------------------------------------------
Ridx1Ridx2
-----------------------------------------------------
AllAll6.467.360.784.56
id_1F0.760.440.120.33
id_1M1.760.980.402.24
id_2F1.490.31-0.21-0.85
id_2M1.870.95-0.98-0.15
id_3F-2.550.860.65-0.74
id_3M-0.100.140.411.45

출처 : 데이터사이언스 스쿨(datascienceschool.net)