-
10 minutes to pandas - Reshaping데이터 분석/Pandas 2022. 3. 30. 23:13
Hierarchical Indexing (MultiIndex)
In essence, it enables you to store and manipulate data
with an arbitrary number of dimensions
in lower dimensional data structures like Series (1d) and DataFrame (2d)MultiIndex 의 효용
The reason that the MultiIndex matters is that it can allow you to do grouping, selection
when loading data from a file, you may wish to generate your own MultiIndex when preparing the data set.Index is like an address, that’s how any data point across the dataframe or series can be accessed.
Indices made it very easy to bring more information easily
Index make filtering very easy and also give you space to move forward and backwards in your dataCreating a MultiIndex (hierarchical index) object
A MultiIndex can be created from
- a list of arrays (using MultiIndex.from_arrays()),
- an array of tuples (using MultiIndex.from_tuples()),
- a crossed set of iterables (using MultiIndex.from_product()),
- or a DataFrame (using MultiIndex.from_frame()).df = pd.DataFrame( [["bar", "one"], ["bar", "two"], ["foo", "one"], ["foo", "two"]], columns=["first", "second"], ) pd.MultiIndex.from_frame(df) ''' MultiIndex([('bar', 'one'), ('bar', 'two'), ('foo', 'one'), ('foo', 'two')], names=['first', 'second']) '''
iterables = [["bar", "baz", "foo", "qux"], ["one", "two"]] pd.MultiIndex.from_product(iterables, names=["first", "second"]) ''' MultiIndex([('bar', 'one'), ('bar', 'two'), ('baz', 'one'), ('baz', 'two'), ('foo', 'one'), ('foo', 'two'), ('qux', 'one'), ('qux', 'two')], names=['first', 'second']) '''
index 는 행과 열 모두에 적용할 수 있다.
(물론 DataFrame 을 생성할 때에는 index 는 행, columns 는 열이지만!)df = pd.DataFrame( np.random.randn(3, 4), index=["A", "B", "C"], columns=index[:4] )
그리고 행과 열의 위치를 이렇게 바꿀 수도 있다.
df2 = df.T df2
Indexing and Selecting data
Using loc
Tuple 은 Hierarcy 로 계층을 나타낼 수 있다.
[("bar", "two")] 는 level = 0 는 "bar", level = 1 는 "two" 인 인덱스를 지칭한다.List 는 하나의 차원에서 여러 키를 지정할 때 사용한다.
[["bar", "baz"]] 는 level = 0 에서 "bar", "baz" 인 인덱스를 지칭한다.# level = 0 1차원까지 지정 df.loc["bar"] df.loc["baz":"foo"] df.loc[["bar", "baz"]] # level = 1 2차원까지 지정 df.loc[("bar", "two")] df.loc[("baz", "two"):"foo"] df.loc[("baz", "two"):("qux", "one")] df.loc[[("bar", "two"), ("qux", "one")]] # level = 0 의 특정 값과 level = 1 의 특정값 df.loc[(['bar', 'baz'], 'one'), 'A'] df.loc[(['bar', 'baz'], 'one'), ] #, 필수 df.loc[(['bar', 'baz'], 'one'), :] #, 필수
# You should specify all axes in the .loc specifier
.loc 에서는 가급적 모든 축의 정보를 명시해야한다.# 에러를 발생시키는 대표적인 사례들 df.loc[(['bar', 'baz'], 'one')] #, 필수 df.loc[('bar':'qux', 'one'), ] # () 안에서 : 사용불가
Using slicer
You can use slice(None) to select all the contents of that level.
You do not need to specify all the deeper levels, they will be implied as slice(None)..loc[] 의 대괄호 내부에서 행과 열을 쉼표로 구분한다. 필요할 경우 () 로 행과 열을 감싼다.
첫번째 level 부터 명시한다. () 안의 , 로 차원을 구분한다.dfmi.loc[(slice("A1", "A3"), slice(None), ["C1", "C3"]), :] # 행 level = 0 슬라이싱: 'A1' 이상 'A3' 이하 # 행 level = 1 전체 # 행 level = 2 배열: 'C1' 과 'C3' # 행 level = 3 전체: 별도 표기 없음 dfmi.loc["A1", (slice(None), "foo")] # 행 level = 0 단수: 'A1' # 열 level = 0 전체 # 열 level = 1 단수: 'foo'
pd.IndexSlice
idx 로 시작하는 [] 배열이므로, 조금 더 가독성이 좋다.
뒤의 차원은 생략하면 된다.idx = pd.IndexSlice dfmi.loc[idx[:, :, ["C1", "C3"]], idx[:, "foo"]] # 행 level = 0 전체 / level = 1 전체 / level = 2 C1, C3 # 컬럼 level = 0 전체, level = 1 foo
mask 조건에 부합하는 인덱스만 필터링
특정 값이 조건에 만족하는 경우만 필터링 할 수 있다.
mask = dfmi[('a', 'foo')] > 200 dfmi.loc[idx[mask, :, ['C1', 'C3']], idx[:, 'foo']]
xs()
xs (cross-section) 을 통해 특정 level 차원을 지정할 수 있다.
level 만 잘 명시해주면 되니, 다차원에서 헷갈려할 필요가 없다고.df.xs("one", level="second") # df.loc[(slice(None), "one"), :] df.xs("one", level="second", axis=1) # df.loc[:, (slice(None), "one")] df = df.T df.xs(("one", "bar"), level=("second", "first"), axis=1) # 생략한 level 에 대해서도 표시하거나, 표시하지 않거나 df.xs('one', level='second', axis=1, drop_level = True) df.xs('one', level='second', axis=1, drop_level = False)
Index 정리
set_index
제시되는 인덱스로 설정한다.
dfm = pd.DataFrame( {"jim": [0, 0, 1, 1], "joe": ["x", "x", "z", "y"], "jolie": np.random.rand(4)} ) dfm = dfm.set_index(["jim", "joe"]) dfm ''' jolie jim joe 0 x 0.490671 x 0.120248 1 z 0.537020 y 0.110968 '''
reset_index
인덱스를 0, 1, 2 등 RangeIndex 로 설정한다. (drop = False, default)
drop 은 하지 않는다! 다른 정보가 더 필요할 때 'key' 로 사용할 수 있기 때문이다.df.reset_index()
stack, unstack
열과 행 axis 를 바꿔본다.
tuples = list( zip( *[ ["bar", "bar", "baz", "baz", "foo", "foo", "qux", "qux"], ["one", "two", "one", "two", "one", "two", "one", "two"], ] ) ) index = pd.MultiIndex.from_tuples(tuples, names=["first", "second"]) df = pd.DataFrame(np.random.randn(4, 2), index=index, columns=["A", "B"]) df ''' A B first second bar one -0.727965 -0.589346 two 0.339969 -0.693205 baz one -0.339355 0.593616 two 0.884345 1.591431 ''' stacked = df.stack() # 열을 행으로! stacked ''' first second bar one A -0.727965 B -0.589346 two A 0.339969 B -0.693205 baz one A -0.339355 B 0.593616 two A 0.884345 B 1.591431 dtype: float64 ''' stacked.unstack() # index 의 마지막 차원을 열로 stacked.unstack(1) # index 의 level = 1 차원을 열로
reindex
데이터와 인덱스를 독립적으로 생각하자.
해당 인덱스에 대한 데이터가 없으면 기본적으로 NaN 이다.index = ['Firefox', 'Chrome', 'Safari', 'IE10', 'Konqueror'] df = pd.DataFrame({'http_status': [200, 200, 404, 404, 301], 'response_time': [0.04, 0.02, 0.07, 0.08, 1.0]}, index=index) df ''' http_status response_time Firefox 200 0.04 Chrome 200 0.02 Safari 404 0.07 IE10 404 0.08 Konqueror 301 1.00 ''' new_index = ['Safari', 'Iceweasel', 'Comodo Dragon', 'IE10', 'Chrome'] df.reindex(new_index) ''' http_status response_time Safari 404.0 0.07 Iceweasel NaN NaN # 값이 없는 인덱스 NaN Comodo Dragon NaN NaN # 값이 없는 인덱스 NaN IE10 404.0 0.08 Chrome 200.0 0.02 ''' df.reindex(new_index, fill_value=0) ''' http_status response_time Safari 404 0.07 Iceweasel 0 0.00 # 값이 없는 인덱스 NaN Iceweasel 0 0.00 # 값이 없는 인덱스 NaN Comodo Dragon 0 0.00 IE10 404 0.08 Chrome 200 0.02 ''' df.reindex(['http_status', 'user_agent'], axis="columns") http_status user_agent Firefox 200 NaN Chrome 200 NaN Safari 404 NaN IE10 404 NaN Konqueror 301 NaN
align
두개의 차원을 맞춘다.
level=0 일 경우, 행의 차원을 맞춘다. (없는 값은 default broadcast 한다. 다른 값을 지정해줄 수도 있다. )
df = pd.DataFrame( [[1, 2, 3, 4], [6, 7, 8, 9]], columns=["D", "B", "E", "A"], index=[1, 2] ) other = pd.DataFrame( [[10, 20, 30, 40], [60, 70, 80, 90], [600, 700, 800, 900]], columns=["A", "B", "C", "D"], index=[2, 3, 4], ) df ''' D B E A 1 1 2 3 4 2 6 7 8 9 ''' other ''' A B C D 2 10 20 30 40 3 60 70 80 90 4 600 700 800 900 ''' left, right = df.align(other, join="outer", axis=1) # 명시된 axis, 열에 대해 index 를 맞춘다. (A, B, C, D, E)로. 없는 값은 NaN 이다. left ''' A B C D E 1 4 2 NaN 1 3 2 9 7 NaN 6 8 ''' right ''' A B C D E 2 10 20 30 40 NaN 3 60 70 80 90 NaN 4 600 700 800 900 NaN ''' left, right = df.align(other, join="outer", axis=0) # 명시된 axis, 행에 대해 index 를 맞춘다. (1,2,3,4)로. 없는 값은 NaN 이다. left ''' D B E A 1 1.0 2.0 3.0 4.0 2 6.0 7.0 8.0 9.0 3 NaN NaN NaN NaN 4 NaN NaN NaN NaN ''' right ''' A B C D 1 NaN NaN NaN NaN 2 10.0 20.0 30.0 40.0 3 60.0 70.0 80.0 90.0 4 600.0 700.0 800.0 900.0 ''' left, right = df.align(other, join="outer", axis=None) # 전체 axis 에 대해 index 를 맞춘다. 없는 값은 NaN 이다. left ''' A B C D E 1 4.0 2.0 NaN 1.0 3.0 2 9.0 7.0 NaN 6.0 8.0 3 NaN NaN NaN NaN NaN 4 NaN NaN NaN NaN NaN ''' right ''' A B C D E 1 NaN NaN NaN NaN NaN 2 10.0 20.0 30.0 40.0 NaN 3 60.0 70.0 80.0 90.0 NaN 4 600.0 700.0 800.0 900.0 NaN '''
다차원의 reindexing 과 alignment
다차원에서 index 가 변경될 경우, 차원에 맞게 broadcasting (aka. 자기복제) 된다.
midx = pd.MultiIndex( levels=[["zero", "one"], ["x", "y"]], codes=[[1, 1, 0, 0], [1, 0, 1, 0]] ) df = pd.DataFrame(np.random.randn(4, 2), index=midx) df ''' 0 1 one y 1.519970 -0.493662 x 0.600178 0.274230 zero y 0.132885 -0.023688 x 2.410179 1.450520 ''' df2 = df.groupby(level=0).mean() df2 # (2,2) ''' 0 1 one 1.060074 -0.109716 zero 1.271532 0.713416 ''' df2.reindex(df.index, level=0) # (2,2) --> (4,2) 값은 broadcasting (차원에 맞게 복제) 0 1 one y 1.060074 -0.109716 x 1.060074 -0.109716 zero y 1.271532 0.713416 x 1.271532 0.713416 # aligning df_aligned, df2_aligned = df.align(df2, level=0) # df (4,2), df2 (2,2) df_aligned # (4,2) --> (4,2) 그대로 ''' 0 1 one y 1.519970 -0.493662 x 0.600178 0.274230 zero y 0.132885 -0.023688 x 2.410179 1.450520 ''' df2_aligned # (2,2) --> (4,2) 값은 broadcasting (차원에 맞게 복제) ''' 0 1 one y 1.060074 -0.109716 x 1.060074 -0.109716 zero y 1.271532 0.713416 x 1.271532 0.713416 '''
'데이터 분석 > Pandas' 카테고리의 다른 글
pandas.DataFrame.replace, where, mask (0) 2022.05.24 10 minutes to pandas - Pivot Tables (0) 2022.04.01 10 minutes to pandas - Group by (split - apply - combine) (0) 2022.03.29 10 minutes to pandas - Merge & Join (0) 2022.03.27 10 minutes to pandas - 결측치 처리 (0) 2022.03.20