ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 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 data

     

     

    Creating 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
    '''

     

     

    댓글

Designed by Tistory.