데이터프레임 인덱스 설정 및 제거¶
때로는 데이터프레임에 인덱스로 들어가 있어야 할 데이터가 일반 데이터 열에 들어가 있거나 반대로 일반 데이터 열이어야 할 것이 인덱스로 되어 있을 수 있다. 이 때는 set_index 명령이나 reset_index 명령으로 인덱스와 일반 데이터 열을 교환할 수 있다.
set_index : 기존의 행 인덱스를 제거하고 데이터 열 중 하나를 인덱스로 설정
reset_index : 기존의 행 인덱스를 제거하고 인덱스를 데이터 열로 추가
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
A | 0.55 | 0.65 | 0.79 |
B | 0.72 | 0.44 | 0.53 |
C | 0.6 | 0.89 | 0.57 |
D | 0.54 | 0.96 | 0.93 |
E | 0.42 | 0.38 | 0.07 |
set_index 메서드로 특정한 열을 인덱스로 설정할 수 있다. 이 때 기존의 인덱스는 없어진다.
df2 = df1.set_index("C1") df2
0.55 | 0.65 | 0.79 |
0.72 | 0.44 | 0.53 |
0.6 | 0.89 | 0.57 |
0.54 | 0.96 | 0.93 |
0.42 | 0.38 | 0.07 |
마찬가지로 C2열을 인덱스로 지정하면 기존의 인덱스는 사라진다.
0.65 | 0.79 |
0.44 | 0.53 |
0.89 | 0.57 |
0.96 | 0.93 |
0.38 | 0.07 |
반대로 reset_index 메서드를 쓰면 인덱스를 보통의 자료열로 바꿀 수도 있다. 이 때 인덱스 열은 자료열의 가장 선두로 삽입된다. 데이터프레임의 인덱스는 정수로 된 디폴트 인덱스로 바뀐다.
A | 0.55 | 0.65 | 0.79 |
B | 0.72 | 0.44 | 0.53 |
C | 0.6 | 0.89 | 0.57 |
D | 0.54 | 0.96 | 0.93 |
E | 0.42 | 0.38 | 0.07 |
reset_index 메서드를 호출할 때 인수 drop=True 로 설정하면 인덱스 열을 보통의 자료열로 올리는 것이 아니라 그냥 버리게 된다.
df2.reset_index(drop=True)
0.55 | 0.65 | 0.79 |
0.72 | 0.44 | 0.53 |
0.6 | 0.89 | 0.57 |
0.54 | 0.96 | 0.93 |
0.42 | 0.38 | 0.07 |
연습 문제 4.5.1
5명의 학생의 국어, 영어, 수학 점수를 나타내는 데이터프레임을 다음과 같이 만든다.
학생 이름을 나타내는 열을 포함시키지 않고 데이터프레임 df_score1 을 생성한 후, df_score1.index 속성에 학생 이름을 나타내는 열을 지정하여 인덱스를 지정한다. reset_index 명령으로 이 인덱스 열을 명령으로 일반 데이터열로 바꾸여 데이터프레임 df_score2을 만든다.
학생 이름을 나타내는 열이 일반 데이터 열을 포함하는 데이터프레임 df_score2에 set_index 명령을 적용하여 다시 학생 이름을 나타내는 열을 인덱스로 변경한다.
다중 인덱스¶
행이나 열에 여러 계층을 가지는 인덱스 즉, 다중 인덱스(multi-index)를 설정할 수도 있다. 데이터프레임을 생성할 때 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
1.76 | 0.40 | 0.98 | 2.24 |
1.87 | -0.98 | 0.95 | -0.15 |
-0.10 | 0.41 | 0.14 | 1.45 |
0.76 | 0.12 | 0.44 | 0.33 |
1.49 | -0.21 | 0.31 | -0.85 |
다중 인덱스는 이름을 지정하면 더 편리하게 사용할 수 있다. 열 인덱스들의 이름 지정은 columns 객체의 names 속성에 리스트를 넣어서 지정한다.
df3.columns.names = ["Cidx1", "Cidx2"] df3
1.76 | 0.40 | 0.98 | 2.24 |
1.87 | -0.98 | 0.95 | -0.15 |
-0.10 | 0.41 | 0.14 | 1.45 |
0.76 | 0.12 | 0.44 | 0.33 |
1.49 | -0.21 | 0.31 | -0.85 |
마찬가지로 데이터프레임을 생성할 때 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
1.76 | 0.40 | 0.98 | 2.24 |
1.87 | -0.98 | 0.95 | -0.15 |
-0.10 | 0.41 | 0.14 | 1.45 |
0.76 | 0.12 | 0.44 | 0.33 |
1.49 | -0.21 | 0.31 | -0.85 |
-2.55 | 0.65 | 0.86 | -0.74 |
행 인덱스와 열 인덱스 교환¶
stack 메서드나 unstack 메서드를 쓰면 열 인덱스를 행 인덱스로 바꾸거나 반대로 행 인덱스를 열 인덱스로 바꿀 수 있다.
stack
열 인덱스 -> 행 인덱스로 변환
unstack
행 인덱스 -> 열 인덱스로 변환
stack 메서드를 실행하면 열 인덱스가 반시계 방향으로 90도 회전한 것과 비슷한 모양이 된다. 마찬가지로 unstack 메서드를 실행하면 행 인덱스가 시계 방향으로 90도 회전한 것과 비슷하다. 인덱스를 지정할 때는 문자열 이름과 순서를 표시하는 숫자 인덱스를 모두 사용할 수 있다.
1.76 | 0.40 |
0.98 | 2.24 |
1.87 | -0.98 |
0.95 | -0.15 |
-0.10 | 0.41 |
0.14 | 1.45 |
0.76 | 0.12 |
0.44 | 0.33 |
1.49 | -0.21 |
0.31 | -0.85 |
-2.55 | 0.65 |
0.86 | -0.74 |
1.76 | 0.98 |
0.40 | 2.24 |
1.87 | 0.95 |
-0.98 | -0.15 |
-0.10 | 0.14 |
0.41 | 1.45 |
0.76 | 0.44 |
0.12 | 0.33 |
1.49 | 0.31 |
-0.21 | -0.85 |
-2.55 | 0.86 |
0.65 | -0.74 |
0.76 | 1.49 | -2.55 | 0.12 | -0.21 | 0.65 | 0.44 | 0.31 | 0.86 | 0.33 | -0.85 | -0.74 |
1.76 | 1.87 | -0.10 | 0.40 | -0.98 | 0.41 | 0.98 | 0.95 | 0.14 | 2.24 | -0.15 | 1.45 |
0.76 | 1.76 | 0.12 | 0.40 | 0.44 | 0.98 | 0.33 | 2.24 |
1.49 | 1.87 | -0.21 | -0.98 | 0.31 | 0.95 | -0.85 | -0.15 |
-2.55 | -0.10 | 0.65 | 0.41 | 0.86 | 0.14 | -0.74 | 1.45 |
다중 인덱스가 있는 경우의 인덱싱¶
데이터프레임이 다중 인덱스를 가지는 경우에는 인덱스 값이 하나의 라벨이나 숫자가 아니라 ()로 둘러싸인 튜플이 되어야 한다. 예를 들어 앞에서 만든 df3 데이터프레임의 경우 다음과 같이 인덱싱할 수 있다.
1.76 | 0.40 | 0.98 | 2.24 |
1.87 | -0.98 | 0.95 | -0.15 |
-0.10 | 0.41 | 0.14 | 1.45 |
0.76 | 0.12 | 0.44 | 0.33 |
1.49 | -0.21 | 0.31 | -0.85 |
0 0.98 1 0.95 2 0.14 3 0.44 4 0.31 Name: (B, C1), dtype: float64
loc 인덱스를 사용하는 경우에도 마찬가지로 튜플을 써야 한다.
df3.loc[0, ("B", "C1")] = 100 df3
1.76 | 0.40 | 100.00 | 2.24 |
1.87 | -0.98 | 0.95 | -0.15 |
-0.10 | 0.41 | 0.14 | 1.45 |
0.76 | 0.12 | 0.44 | 0.33 |
1.49 | -0.21 | 0.31 | -0.85 |
단, iloc 인덱서를 사용하는 경우에는 튜플 형태의 다중인덱스를 사용할 수 없다.
만약 하나의 레벨 값만 넣으면 다중 인덱스 중에서 가장 상위의 값을 지정한 것으로 본다.
1.76 | 0.40 |
1.87 | -0.98 |
-0.10 | 0.41 |
0.76 | 0.12 |
1.49 | -0.21 |
df4 데이터프레임은 다음과 같이 인덱싱할 수 있다.
1.76 | 0.40 | 0.98 | 2.24 |
1.87 | -0.98 | 0.95 | -0.15 |
-0.10 | 0.41 | 0.14 | 1.45 |
0.76 | 0.12 | 0.44 | 0.33 |
1.49 | -0.21 | 0.31 | -0.85 |
-2.55 | 0.65 | 0.86 | -0.74 |
df4.loc[("M", "id_1"), ("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
1.76 | 0.40 | 0.98 | 2.24 |
1.87 | -0.98 | 0.95 | -0.15 |
-0.10 | 0.41 | 0.14 | 1.45 |
0.76 | 0.12 | 0.44 | 0.33 |
1.49 | -0.21 | 0.31 | -0.85 |
-2.55 | 0.65 | 0.86 | -0.74 |
3.23 | 0.39 | 3.68 | 2.28 |
loc를 사용하는 경우에도 튜플이 아닌 하나의 값만 쓰면 가장 상위의 인덱스를 지정한 것과 같다.
1.76 | 0.40 | 0.98 | 2.24 |
1.87 | -0.98 | 0.95 | -0.15 |
-0.10 | 0.41 | 0.14 | 1.45 |
특정 레벨의 모든 인덱스 값을 인덱싱할 때는 슬라이스를 사용한다. 다만 다중 인덱스의 튜플 내에서는 : 슬라이스 기호를 사용할 수 없고 대신 slice(None) 값을 사용해야 한다.
df4.loc[("M", slice(None)), :]
1.76 | 0.40 | 0.98 | 2.24 |
1.87 | -0.98 | 0.95 | -0.15 |
-0.10 | 0.41 | 0.14 | 1.45 |
df4.loc[(slice(None), "id_1"), :]
1.76 | 0.40 | 0.98 | 2.24 |
0.76 | 0.12 | 0.44 | 0.33 |
다중 인덱스의 인덱스 순서 교환¶
다중 인덱스의 인덱스 순서를 바꾸고 싶으면 swaplevel 명령을 사용한다.
swaplevel(i, j, axis)
i와 j는 교환하고자 하는 인덱스 라벨(혹은 인덱스 번호)이고 axis는 0일 때 행 인덱스, 1일 때 열 인덱스를 뜻한다. 디폴트는 행 인덱스이다.
df5 = df4.swaplevel("Ridx1", "Ridx2") df5
1.76 | 0.40 | 0.98 | 2.24 |
1.87 | -0.98 | 0.95 | -0.15 |
-0.10 | 0.41 | 0.14 | 1.45 |
0.76 | 0.12 | 0.44 | 0.33 |
1.49 | -0.21 | 0.31 | -0.85 |
-2.55 | 0.65 | 0.86 | -0.74 |
3.23 | 0.39 | 3.68 | 2.28 |
df6 = df4.swaplevel("Cidx1", "Cidx2", 1) df6
1.76 | 0.40 | 0.98 | 2.24 |
1.87 | -0.98 | 0.95 | -0.15 |
-0.10 | 0.41 | 0.14 | 1.45 |
0.76 | 0.12 | 0.44 | 0.33 |
1.49 | -0.21 | 0.31 | -0.85 |
-2.55 | 0.65 | 0.86 | -0.74 |
3.23 | 0.39 | 3.68 | 2.28 |
다중 인덱스가 있는 경우의 정렬¶
다중 인덱스가 있는 데이터프레임을 sort_index로 정렬할 때는 level 인수를 사용하여 어떤 인덱스를 기준으로 정렬하는지 알려주어야 한다.
3.23 | 0.39 | 3.68 | 2.28 |
0.76 | 0.12 | 0.44 | 0.33 |
1.76 | 0.40 | 0.98 | 2.24 |
1.49 | -0.21 | 0.31 | -0.85 |
1.87 | -0.98 | 0.95 | -0.15 |
-2.55 | 0.65 | 0.86 | -0.74 |
-0.10 | 0.41 | 0.14 | 1.45 |
df6.sort_index(axis=1, level=0)
1.76 | 0.98 | 0.40 | 2.24 |
1.87 | 0.95 | -0.98 | -0.15 |
-0.10 | 0.14 | 0.41 | 1.45 |
0.76 | 0.44 | 0.12 | 0.33 |
1.49 | 0.31 | -0.21 | -0.85 |
-2.55 | 0.86 | 0.65 | -0.74 |
3.23 | 3.68 | 0.39 | 2.28 |
연습 문제 4.5.2
A 반 학생 5명과 B반 학생 5명의 국어, 영어, 수학 점수를 나타내는 데이터프레임을 다음과 같이 만든다.
“반”, “번호”, “국어”, “영어”, “수학” 을 열로 가지는 데이터프레임 df_score3을 만든다.
df_score3을 변형하여 1차 행 인덱스로 “반”을 2차 행 인덱스로 “번호”을 가지는 데이터프레임 df_score4을 만든다.
데이터 프레임 df_score4에 각 학생의 평균을 나타내는 행을 오른쪽에 추가한다.
df_score3을 변형하여 행 인덱스로 “번호”를, 1차 열 인덱스로 “국어”, “영어”, “수학”을, 2차 열 인덱스로 “반”을 가지는 데이터프레임 df_score5을 만든다.
데이터 프레임 df_score5에 각 반별 각 과목의 평균을 나타내는 행을 아래에 추가한다.