20. pandas 추가 – 계층적 인덱싱(정렬함수, 통계함수적용, 인덱스와 칼럼 전환(stack,unstack))
계층적 인덱스 - 정렬함수
물류회사에서 고객정보를 관리하는데, 고객의 주소를 index로 하여 데이터를 관리한다고 가정해보자.
가장 계층인 시(군)에서부터 작은 계층인 번지까지 하나씩 내려올 것이다. ex> 서울특별시 강남구 삼성로 212
이렇게 계층적인 정보를 index로 사용하고 싶을 때, 이것을 계층적 인덱싱이라고 한다.
10개 랜덤수를 Series를 만드는데, 인덱스를 2차원 리스트(혹은 2차원 array)로 주면, 층이 2개인 index를 가지게 된다.
s = pd.Series(np.random.randn(10),
index=[["a", "a", "a", "b", "b", "b", "c", "c", "d", "d"],
[1, 2, 3, 1, 2, 3, 1, 2, 2, 3]])
첫번 째 층에는 a부터 d까지, 2번째 층에서는 1~3까지의 정수가 나와있다. 실제로 Series.의 index를 확인해보면 MultiIndex라고 표시되어있다.
이렇게 계층적 인덱스가 있을 때, 인덱싱을 어떻게 수행하는지 알아보자.
가장 바깥층 인덱스부터 인덱싱하는 것이 원칙이다.
- s["b"] 를 통해서, 정수로 성분 n-1번째 인덱싱 으로 쓰이는 s[ ]에, 가장 바깥층을 index명을 적어서, 그 하위계층의 인덱스+성분을 얻게 된다.
(원래는 정수를 넣어서 s[0]을 하면 1번째 성분, s[1]을 하면 2번째 성분이 나오는 Series의 성분인덱싱으로 쓰이는 곳이었다) - 가장 바깥층의 index명으로 범위인덱싱도 할 수 있다.
s["b":"c"] - 인덱스란에 ()소괄호를 넣어, (상위계층,하위계층) 형식으로 넣으면 하위 계층까지 지정해서 인덱싱할 수 있다.
s[ ("b", 3)]
확인 결과, 소괄호 없어도 된다. [ , ]인덱싱처럼 콤마를 주어 하위계층을 입력하면 [첫번째 층, 두번째 층]을 지정해서 인덱싱할 수 있다.
s[ "b", 3]
(원래, []안에 인자가 여러개 들어갔을 때, 같은 위상에 있는 연속적인 값을 입력할 때는 소괄호를 통해 입력한다)
아! 그래서 행 인덱싱은 따로 .loc[]를 이용했구나, 2차원의 경우 열인덱싱에서 계층적인덱싱하라고??
열인덱싱란에 [행,렬]을 적어봤자 행렬이 아니라, 계층적인덱싱이다.
.loc[]의 인덱싱란에 적어야 [행,렬]이 된다. - 마찬가지로, s[첫번째 층 인덱스, 두번째층 인덱스]로 인덱싱하는데, 콜론을 이용하여, 2번째 층의 인덱스가 2인 것만 뽑아낼 수 있다.
s[ : , 2 ]
DataFrame에서도 계층적 인덱싱을 해보자.
Series에서는, index만 2차원 리스트형식으로 줬지만, DataFrame에서는 columns까지 2차원으로 주자.
df = pd.DataFrame(np.arange(12).reshape((4, 3)),
index=[["a", "a", "b", "b"],
[1, 2, 1, 2]],
columns=[["Seoul", "Seoul", "Busan"],
["Green", "Red", "Green"]])
- 복수 계층의 인덱스 와 칼럼에 이름을 붙힐 때는, 파이썬의 리스트 형식으로 주면 된다.
df.index.names = [ "key1", "key2"]
df.columns.names = ["city","color"]
DataFrame의 열에 대한 계층적 인덱싱을 해보자.
- DataFrame의 열도 가장 상위계층을 먼저 인덱싱해야한다.
df["Seoul"] - 이제 상위계층의 하위계층까지 지정해서 인덱싱해보자. 열의 최하위계층까지 간다면 하나의 열만 추출하므로 Series형태로 나올 것이다.
인덱스는 그대로 계층적 인덱싱을 하고 있다.
df["Seoul", "Green"]
행의 인덱싱도 기존 인덱싱과 유사하게 최상위계층부터 인덱싱해야한다.
- df.loc["a"] 를 통해 상위계층 a를 인덱스로 가지는 dataFrame과
df.loc["a", 1]를 통해 상위a , 하위 1이라는 인덱스를 가지는 하나의 행-> Series형태로 얻을 수 있다.
이때, Series의 인덱스는 기존 DataFrame의 계층적인 컬럼들이 인덱스로 들어가는 것을 확인할 수 있다.
.loc를 이용해서, 행과 열을 동시에 계층적 인덱싱을 할 수 있다( 다만 각 행, 렬에서 순차적으로)
- 행과 열을 동시에 계층적 인덱싱할 때, 소괄호()를 통해서 상위->하위계층으로 들어갈 수 있다.
df.loc["b", ("Seoul","Red")]
상위 b행 , 상위 Seoul열-> 하위 Red열 인덱싱
df.loc[("b", 2), "Busan"] 를 통해서, index( b->2) / columns Busan만 인덱싱 할 수 있다. - 행과 열을 동시에 인덱싱할 때는, 모든 계층을 다 타고 내려가면, 특정 성분이 나올 것이다.
(행 or 열 하나만 인덱싱하여 최하위까지 가면, Series가 나온다)
df.loc[("b", 1), ("Seoul", "Green")]
계층적 인덱스에도 인덱스를 기준으로 정렬을 할 수 있다.
이전시간에는 df.sort_index() 만 호출하면 인덱스가 자동으로 오름차순으로 정렬이 됬었다.
여기에 level이라는 인자를 주어, 해당하는 인덱스의 층수(정수 인덱스 : 0,1,2,…)를 주면, 그 층의 인덱스를 오름차순으로 정렬할 수 있다.
(sort_index()에 axis인자를 주지 않으면, 열에 따른 행 방향(axis=0) 기준으로 정렬한다. )
- df.sort_index(axis=0, level=1) 를 통해,
행 방향(↓)으로 행index가 + 2번째 층의 인덱스(key2)가 오름차순 되도록 정렬시킨다. - level인자에 정수 인덱스가 번거롭다면, 층의 이름을 명시해서 사용할 수 있다.
- 행에 따른 열 방향(→)으로 columns을 오름차순 정렬하면서, 계층을 명시할 수 있다.
df.sort_index(axis=1, level=0) 를 통해 1번째 계층인 city를 알파벳 순서로 busan->seoul 순으로 오름차순 정렬 된다.
df.sort_index(axis=1, level=1) 를 통해 2번째 계층인 color 오름차순으로 green->red 순으로 정렬된다.
계층적 인덱싱에서, 성분(값)기준 정렬을 할 수 있다.
이전 13강에서 sort_values( by ="칼럼명")를 통해 특정 칼럼의 성분을 기준으로 오름차순으로 정렬했었다.
(sort_values by = 칼럼명 은 하나로 묶여서 생각하자)
여기에 ()소괄호를 이용해서 계층적 인덱싱을 하면 된다.
계층적 인덱스 - 통계함수
계층적 인덱스에 통계함수를 적용해보자.
이전에는 df.sum(axis=0) 을 통해서 각 열들에 대해 행 방향(↓)의 합을 구했다.
여기에 level인자를 추가해서 <어디서 계산을 끊어서 갈지>기준이 되는 index계층을 지정하면 된다.
만약 axis=0이라면, 열에 따라 행방향으로 합을 구하므로, 끊어주는 기준은 행(index)이 된다.
만약 axis=1이라면, 행에 따라 열방향으로 합을 구하므로, 끊어주는 기준은 열(columns)이 된다.
즉, 방향에 따라 <끊어서 계산하는 기준>이 level이다.
axis=0으로서, 각 열들(행 방향)으로 합은 구하기 때문에 ---> level은 열이 아닌 index가 기준이 된다.(어디까지의 합인지 끊어주는 기준)
- df.sum(axis=0, level=0) 을 통해, 각 열들에 대해 행방향으로 합을 각각 구하는데,
어디까지의 합인지 끊어주는 기준은 level=0인 1번째 index 층이 기준이 된다. 즉, a와 b를 구분해서 각 열의 성분들의 합을 구한다. - df.sum(axis=0, level=1) 를 통해, key2라는 열에는 a-1,2/ b-1,2의 계층이 있으나, 그것을 무시하고
key2 index의 1과 2가 끊어주는 기준이 된다( 1번째 계층 a,b는 무시되어 서로 섞인다)
level에 원하는 인덱스 또는 컬럼의 특정계층 이름(name)을 명시해서 통계함수를 적용할 수 있다.(컬럼명 아님!)
만약 axis=0이라면, 열에 따라 행방향으로 합을 구하므로, 끊어주는 기준은 행(index)이 된다.
만약 axis=1이라면, 행에 따라 열방향으로 합을 구하므로, 끊어주는 기준은 열(columns)이 된다.
즉, 방향에 따라 끊어서 계산하는 기준이 level이다.
DataFrame 특정열의 성분을 계층적 인덱스로 변환
기존의 DataFrame의 특정열에 포함된 값을 계층적 인덱스로 변환하거나 혹은 그 반대로 할 수가 있다.
df2 = pd.DataFrame({'a': range(7), 'b': range(7, 0, -1),
'c': ['one', 'one', 'one', 'two', 'two', 'two', 'two'],
'd': [0, 1, 2, 0, 1, 2, 3]})
*** range(0부터 총 갯수) / range( 시작수, 포함되지않는 끝수, 폭) : 내림차순으로 하기 위해서는 폭을 -1로 주어야한다!
c와 d열의 성분을 계층적 인덱스로 바꿔보자.
- set_index()함수에 리스트[]형식으로, 계층순서대로 컬럼명을 주면된다.
(my : 인덱싱할 때는 [ ] 안에 소괄호로 계층을 주고 / 각종 함수에서는 ( )안에 리스트로 계층을 주네?!)
df3 = df2.set_index( ["c", "d"] )
그럼 c의 성분들에 대해 d의 성분들이 알아서 박힌다. 그리고 c, d열은 사라진다 - c열과 d열을 계층적 인덱스로 주면서, 기존의 c열과 d열을 유지하면서 c,d열을 인덱스로 내려주고 싶다면
set_index()함수의 인자에, 열->index 으로 내려가는 drop을 False로 주면 된다.
df2.set_index(["c","d"], drop=False)
현재 존재하는 계층적 인덱스를-> 열로 올리면서 동시에 기본 정수를 index로 주는 함수는 reset_index()함수다.
DataFrame 인덱스와 칼럼 전환
DataFrame 모양을 변형하기 위해 아래 예시 데이터를 보자.
df4 = pd.DataFrame(np.arange(6).reshape((2, 3)),
index=['Seoul', 'Busan'],
columns=['one', 'two', 'three'])
df4.index.name = "city"
df4.columns.name = "number"
계층적 인덱스는 아니다. 이 때, stack()함수를 사용하면, DataFrame의 최하층 컬럼자체가 –> 인덱스의 최하위 index 층으로 붙게 되면서, Series가 된다. 여기서는 컬럼이 단일층이므로, 해당 칼럼이 넘어가버려 Series가 되는 것이다.
- df4.stack() 를 통해 단일컬럼(number)가 단일 인덱스(city)의 하위계층으로 붙게 되는 것을 확인할 수 있다.
기존의 칼럼이 사라지면서, Series의 형태가 되어버린다.
*** 주의할 점은, stack()함수와 set_index()의 작동방식이 다른 것이다.
(1) set_index( [ “컬럼1”, “컬럼2” ])는 컬럼이 내려와서 기존의 인덱스를 <대체> 해버린다.
drop=False로, 칼럼1,2를 살릴 수 도 있다.
(2) stack()함수는 컬럼의 최하위계층이 기존 인덱스의 <최하위계층으로 내러와서 붙는다> - df5.unstack()을 통해서, 인덱스의 최하위 계층(number)를 –> 칼럼의 최하위계층으로 올린다
만일 계층적 인덱스 중 특정계층의 index를 columns으로 올리고 싶다면 level인자로 지정해준다.
- df5.unstack(level=0)를 통해, 첫번째 계층의 index를 --> 컬럼으로 올린다.
이전에는 df5.unstack()만하여 최하위 계층의 index가 --> 컬럼으로 올라갔었다. - level인자를 특정 인덱스명으로 지정해줘도 똑같이 컬럼으로 특정index층이 올라가는 것을 확인할 수 있다.
이제 2개의 Series를 만들고, concat()함수를 통해, s1밑에다가 s2를 행으로서, 단순연결하여 s3의 Series를 만드는데
keys인자를 통해 s1과 s2의 각각 index이름이 one과 two로 구분된다.
기존에 인덱스가 있는 series들의 합이므로, keys로 준 인덱스명이 상위계층의 인덱스가 되어버림?!!
(axis=1로서 행방향으로 열을 각각 붙혔다면, 칼럼명이 생성되는데 여기서는 index가 있는 상태에서 또 인덱스명으로 들어가므로, 계층적인덱스가 되어버린다)
(concat()함수는 axis인자가 default일 때, 열에 따른 행 방향으로 붙힘, 즉 밑에다가 행으로 붙힌다.)
s1 = pd.Series([0, 1, 2, 3], index=['a', 'b', 'c', 'd'])
s2 = pd.Series([4, 5, 6], index=['c', 'd', 'e'])
s3 = pd.concat([s1, s2], keys=["one", "two"])
이제 좀 더 복잡한 DataFrame을 stack과 unstack()함수를 살펴보자.
기존 계층적 인덱스를 가지고 있는 Series df5를 2개로 나누어 df를 만들고, 거기다가 name이라는 컬럼 전체이름에, 각각 left, right라는 컬럼명도 지정해주었다.
df6 = pd.DataFrame({"left": df5, "right": df5 + 5},
columns=["left", "right"])
df6.columns.name = "side"
- df6.unstack()을 통해 최하위 index인 number 라는 인덱스명이 –> 칼럼의 최하위층로 올라간다.
- unstack()을 할 때, level인자를 주어서, 첫번째 계층(city)을 최하위 칼럼으로 올릴 수 도 있다.
df6.unstack(level=0) - stack과 unstack을 동시에 할 수 도 있다.
df6.unstack(level="city").stack(level="side") 을 통해,
city 인덱스는 unstack하여 칼럼으로 올리고, 칼럼명 side는 stack()을 통해 index 최하위계층으로 내려보자.
데이터 분석에 있어서, stack/unstack 등의 계층적 인덱싱을 하면 복잡해지므로, 사실상 비추하는 분야이다.
'빅데이터 관련 프로그래밍 > Python - bigdata(pandas 기초)' 카테고리의 다른 글
22. pandas 추가 – 데이터 그룹화 함수 이해하기 (0) | 2018.03.05 |
---|---|
21. pandas 추가 – DataFrame 데이터 변형(중복행 제거/ 매핑/ 치환/ 카테고리 자료형) (1) | 2018.03.03 |
19. pandas 추가 – 데이터 합치기 2가지 방식(merging, concatenating) (1) | 2018.03.01 |
18. Matplotlib - 기타 데이터 시각화 라이브러리(Seaborn, Bokeh, Folium) (0) | 2018.02.28 |
참고 : matplotlib 분석 정리 (0) | 2018.02.28 |