팬더-열의 계층 인덱스를 평평하게하는 방법


325

축 1 (열)의 계층 인덱스가있는 데이터 프레임이 있습니다 ( groupby.agg작업에서).

     USAF   WBAN  year  month  day  s_PC  s_CL  s_CD  s_CNT  tempf       
                                     sum   sum   sum    sum   amax   amin
0  702730  26451  1993      1    1     1     0    12     13  30.92  24.98
1  702730  26451  1993      1    2     0     0    13     13  32.00  24.98
2  702730  26451  1993      1    3     1    10     2     13  23.00   6.98
3  702730  26451  1993      1    4     1     0    12     13  10.04   3.92
4  702730  26451  1993      1    5     3     0    10     13  19.94  10.94

평평하고 싶기 때문에 다음과 같이 보입니다 (이름은 중요하지 않습니다-이름을 바꿀 수 있습니다).

     USAF   WBAN  year  month  day  s_PC  s_CL  s_CD  s_CNT  tempf_amax  tmpf_amin   
0  702730  26451  1993      1    1     1     0    12     13  30.92          24.98
1  702730  26451  1993      1    2     0     0    13     13  32.00          24.98
2  702730  26451  1993      1    3     1    10     2     13  23.00          6.98
3  702730  26451  1993      1    4     1     0    12     13  10.04          3.92
4  702730  26451  1993      1    5     3     0    10     13  19.94          10.94

어떻게해야합니까? (나는 많은 것을 시도했지만 아무 소용이 없었습니다.)

제안에 따라 여기에 dict 형식의 머리가 있습니다.

{('USAF', ''): {0: '702730',
  1: '702730',
  2: '702730',
  3: '702730',
  4: '702730'},
 ('WBAN', ''): {0: '26451', 1: '26451', 2: '26451', 3: '26451', 4: '26451'},
 ('day', ''): {0: 1, 1: 2, 2: 3, 3: 4, 4: 5},
 ('month', ''): {0: 1, 1: 1, 2: 1, 3: 1, 4: 1},
 ('s_CD', 'sum'): {0: 12.0, 1: 13.0, 2: 2.0, 3: 12.0, 4: 10.0},
 ('s_CL', 'sum'): {0: 0.0, 1: 0.0, 2: 10.0, 3: 0.0, 4: 0.0},
 ('s_CNT', 'sum'): {0: 13.0, 1: 13.0, 2: 13.0, 3: 13.0, 4: 13.0},
 ('s_PC', 'sum'): {0: 1.0, 1: 0.0, 2: 1.0, 3: 1.0, 4: 3.0},
 ('tempf', 'amax'): {0: 30.920000000000002,
  1: 32.0,
  2: 23.0,
  3: 10.039999999999999,
  4: 19.939999999999998},
 ('tempf', 'amin'): {0: 24.98,
  1: 24.98,
  2: 6.9799999999999969,
  3: 3.9199999999999982,
  4: 10.940000000000001},
 ('year', ''): {0: 1993, 1: 1993, 2: 1993, 3: 1993, 4: 1993}}

5
df[:5].to_dict()다른 사람들이 데이터 세트에서 읽을 수 있도록 예제로 출력을 추가 할 수 있습니까?
Zelazny7

좋은 생각. 코멘트가 너무 길어서 위를 했습니까?
로스 R

이를 위해 전용 메소드를 구현하기 위해 이슈 트래커 pandas 대한 제안 이 있습니다.
joelostblom

2
@joelostblom 그리고 실제로 구현되었습니다 (팬더 0.24.0 이상). 나는 답변을 게시 했지만 본질적으로 당신은 할 수 있습니다 dat.columns = dat.columns.to_flat_index(). 내장 팬더 기능.
onlyphantom

답변:


471

가장 쉬운 방법은 열을 최상위 수준으로 설정하는 것입니다.

df.columns = df.columns.get_level_values(0)

참고 : to 레벨에 이름이 있으면 0이 아닌이 이름으로 액세스 할 수도 있습니다.

.

joinMultiIndex를 하나의 Index로 결합 / 열에 문자열 항목이 있다고 가정하면 다음을 수행 할 수 있습니다.

df.columns = [' '.join(col).strip() for col in df.columns.values]

참고 : strip두 번째 색인이없는 경우에는 공백이 있어야 합니다.

In [11]: [' '.join(col).strip() for col in df.columns.values]
Out[11]: 
['USAF',
 'WBAN',
 'day',
 'month',
 's_CD sum',
 's_CL sum',
 's_CNT sum',
 's_PC sum',
 'tempf amax',
 'tempf amin',
 'year']

14
df.reset_index (inplace = True) 가 대안 솔루션이 될 수 있습니다.
Tobias

8
하나의 작은 주석 ... 결합 열 다중 레벨에 _를 사용하려면 .. 이것을 사용할 수 있습니다 ... df.columns = [ '_'. join (col) .strip () df.columns의 col에. 값]
ihightower

30
약간의 수정이 합류 COLS을 위해 밑줄 만 유지 :['_'.join(col).rstrip('_') for col in df.columns.values]
세이지 암스트롱

두 번째 열만 사용하려면 다음과 같이하십시오. df.columns = [df.columns.values의 col에 대해 col [1]]
user3078500 2016 년

1
sum s_CD대신에 사용하려면 s_CD sum할 수 있습니다 df.columns = ['_'.join(col).rstrip('_') for col in [c[::-1] for c in df.columns.values]].
irene

82
pd.DataFrame(df.to_records()) # multiindex become columns and new index is integers only

3
이것은 작동하지만 프로그래밍 방식으로 액세스하기 어렵고 쿼리 할 수없는 열 이름 뒤에 남습니다.
dmeu

1
최신 버전의 팬더에서는 작동하지 않습니다. 0.18에서는 작동하지만 0.20에서는 작동하지 않습니다 (최근 현재)
TH22

1
열 이름을 유지하기위한 @dmeupd.DataFrame(df.to_records(), columns=df.index.names + list(df.columns))
Teoretic

1
그것은 나를 위해 튜플로 열 이름을 유지하고 있으며 내가 사용하는 색인을 유지합니다.pd.DataFrame(df_volume.to_records(), index=df_volume.index).drop('index', axis=1)
Jayen

54

이 글타래에 대한 모든 현재 답변은 약간 날짜가 지났어야합니다. 현재 pandas버전 0.24.0의는 .to_flat_index()당신이 필요하지 않습니다.

팬더 자신의 문서에서 :

MultiIndex.to_flat_index ()

다중 인덱스를 레벨 값을 포함하는 튜플의 인덱스로 변환합니다.

문서에서 간단한 예 :

import pandas as pd
print(pd.__version__) # '0.23.4'
index = pd.MultiIndex.from_product(
        [['foo', 'bar'], ['baz', 'qux']],
        names=['a', 'b'])

print(index)
# MultiIndex(levels=[['bar', 'foo'], ['baz', 'qux']],
#           codes=[[1, 1, 0, 0], [0, 1, 0, 1]],
#           names=['a', 'b'])

적용 to_flat_index():

index.to_flat_index()
# Index([('foo', 'baz'), ('foo', 'qux'), ('bar', 'baz'), ('bar', 'qux')], dtype='object')

기존 pandas열 을 대체하는 데 사용

에 사용하는 방법의 예 datMultiIndex열 이있는 DataFrame입니다 .

dat = df.loc[:,['name','workshop_period','class_size']].groupby(['name','workshop_period']).describe()
print(dat.columns)
# MultiIndex(levels=[['class_size'], ['count', 'mean', 'std', 'min', '25%', '50%', '75%', 'max']],
#            codes=[[0, 0, 0, 0, 0, 0, 0, 0], [0, 1, 2, 3, 4, 5, 6, 7]])

dat.columns = dat.columns.to_flat_index()
print(dat.columns)
# Index([('class_size', 'count'),  ('class_size', 'mean'),
#     ('class_size', 'std'),   ('class_size', 'min'),
#     ('class_size', '25%'),   ('class_size', '50%'),
#     ('class_size', '75%'),   ('class_size', 'max')],
#  dtype='object')

42

Andy Hayden의 대답은 확실히 가장 쉬운 방법입니다. 중복 열 레이블을 피하려면 약간 조정해야합니다.

In [34]: df
Out[34]: 
     USAF   WBAN  day  month  s_CD  s_CL  s_CNT  s_PC  tempf         year
                               sum   sum    sum   sum   amax   amin      
0  702730  26451    1      1    12     0     13     1  30.92  24.98  1993
1  702730  26451    2      1    13     0     13     0  32.00  24.98  1993
2  702730  26451    3      1     2    10     13     1  23.00   6.98  1993
3  702730  26451    4      1    12     0     13     1  10.04   3.92  1993
4  702730  26451    5      1    10     0     13     3  19.94  10.94  1993


In [35]: mi = df.columns

In [36]: mi
Out[36]: 
MultiIndex
[(USAF, ), (WBAN, ), (day, ), (month, ), (s_CD, sum), (s_CL, sum), (s_CNT, sum), (s_PC, sum), (tempf, amax), (tempf, amin), (year, )]


In [37]: mi.tolist()
Out[37]: 
[('USAF', ''),
 ('WBAN', ''),
 ('day', ''),
 ('month', ''),
 ('s_CD', 'sum'),
 ('s_CL', 'sum'),
 ('s_CNT', 'sum'),
 ('s_PC', 'sum'),
 ('tempf', 'amax'),
 ('tempf', 'amin'),
 ('year', '')]

In [38]: ind = pd.Index([e[0] + e[1] for e in mi.tolist()])

In [39]: ind
Out[39]: Index([USAF, WBAN, day, month, s_CDsum, s_CLsum, s_CNTsum, s_PCsum, tempfamax, tempfamin, year], dtype=object)

In [40]: df.columns = ind




In [46]: df
Out[46]: 
     USAF   WBAN  day  month  s_CDsum  s_CLsum  s_CNTsum  s_PCsum  tempfamax  tempfamin  \
0  702730  26451    1      1       12        0        13        1      30.92      24.98   
1  702730  26451    2      1       13        0        13        0      32.00      24.98   
2  702730  26451    3      1        2       10        13        1      23.00       6.98   
3  702730  26451    4      1       12        0        13        1      10.04       3.92   
4  702730  26451    5      1       10        0        13        3      19.94      10.94   




   year  
0  1993  
1  1993  
2  1993  
3  1993  
4  1993

2
감사합니다 Theodros! 이것은 모든 경우를 처리하는 유일한 올바른 솔루션입니다!
CanCeylan

17
df.columns = ['_'.join(tup).rstrip('_') for tup in df.columns.values]

14

그리고 다중 인덱스의 두 번째 수준에서 집계 정보를 유지하려면 다음을 시도하십시오.

In [1]: new_cols = [''.join(t) for t in df.columns]
Out[1]:
['USAF',
 'WBAN',
 'day',
 'month',
 's_CDsum',
 's_CLsum',
 's_CNTsum',
 's_PCsum',
 'tempfamax',
 'tempfamin',
 'year']

In [2]: df.columns = new_cols

new_cols정의되지 않았습니다.
samthebrand

11

map기능 을 사용하기 위해 이것을 수행하는 가장 파이썬적인 방법 .

df.columns = df.columns.map(' '.join).str.strip()

출력 print(df.columns):

Index(['USAF', 'WBAN', 'day', 'month', 's_CD sum', 's_CL sum', 's_CNT sum',
       's_PC sum', 'tempf amax', 'tempf amin', 'year'],
      dtype='object')

f 문자열로 Python 3.6 이상을 사용하여 업데이트하십시오.

df.columns = [f'{f} {s}' if s != '' else f'{f}' 
              for f, s in df.columns]

print(df.columns)

산출:

Index(['USAF', 'WBAN', 'day', 'month', 's_CD sum', 's_CL sum', 's_CNT sum',
       's_PC sum', 'tempf amax', 'tempf amin', 'year'],
      dtype='object')

9

가장 쉽고 직관적 인 솔루션은 get_level_values 사용하여 열 이름을 결합하는 것이 었습니다 . 이렇게하면 동일한 열에서 둘 이상의 집계를 수행 할 때 중복 열 이름이 방지됩니다.

level_one = df.columns.get_level_values(0).astype(str)
level_two = df.columns.get_level_values(1).astype(str)
df.columns = level_one + level_two

열 사이에 구분 기호가 필요한 경우이 작업을 수행 할 수 있습니다. 이것은 두 인덱스 수준의 값을 가진 열의 밑줄 만 포함하는 허용되는 답변에 대한 Seiji Armstrong의 의견과 동일한 내용을 반환합니다.

level_one = df.columns.get_level_values(0).astype(str)
level_two = df.columns.get_level_values(1).astype(str)
column_separator = ['_' if x != '' else '' for x in level_two]
df.columns = level_one + column_separator + level_two

나는 이것이 앤디 헤이든 (Andy Hayden)의 위의 위대한 대답과 같은 것을 알고 있지만,이 방법은 조금 더 직관적이며 기억하기 쉽다 (그래서 초보자 팬더 사용자에게는이 스레드를 계속 언급 할 필요가 없음) .

이 방법은 3 개의 열 수준을 가질 수있는 경우에 더욱 확장 가능합니다.

level_one = df.columns.get_level_values(0).astype(str)
level_two = df.columns.get_level_values(1).astype(str)
level_three = df.columns.get_level_values(2).astype(str)
df.columns = level_one + level_two + level_three

6

모든 대답을 읽은 후에 나는 이것을 생각해 냈습니다.

def __my_flatten_cols(self, how="_".join, reset_index=True):
    how = (lambda iter: list(iter)[-1]) if how == "last" else how
    self.columns = [how(filter(None, map(str, levels))) for levels in self.columns.values] \
                    if isinstance(self.columns, pd.MultiIndex) else self.columns
    return self.reset_index() if reset_index else self
pd.DataFrame.my_flatten_cols = __my_flatten_cols

용법:

주어진 데이터 프레임 :

df = pd.DataFrame({"grouper": ["x","x","y","y"], "val1": [0,2,4,6], 2: [1,3,5,7]}, columns=["grouper", "val1", 2])

  grouper  val1  2
0       x     0  1
1       x     2  3
2       y     4  5
3       y     6  7
  • 단일 집계 방법 : 지정된 결과 변수 소스와 동일합니다 :

    df.groupby(by="grouper").agg("min").my_flatten_cols()
    • df.groupby(by="grouper", as_index = False) 또는 .reset_index () 와 동일.agg(...)
    • ----- before -----
                 val1  2
        grouper         
      
      ------ after -----
        grouper  val1  2
      0       x     0  1
      1       y     4  5
  • 단일 소스 변수, 다중 집계 : 통계에 따라 이름이 지정된 결과 변수 :

    df.groupby(by="grouper").agg({"val1": [min,max]}).my_flatten_cols("last")
    • 와 동일합니다 a = df.groupby(..).agg(..); a.columns = a.columns.droplevel(0); a.reset_index().
    • ----- before -----
                  val1    
                 min max
        grouper         
      
      ------ after -----
        grouper  min  max
      0       x    0    2
      1       y    4    6
  • 여러 변수, 여러 집계 : 이름이 (varname) _ (statname) 인 결과 변수 :

    df.groupby(by="grouper").agg({"val1": min, 2:[sum, "size"]}).my_flatten_cols()
    # you can combine the names in other ways too, e.g. use a different delimiter:
    #df.groupby(by="grouper").agg({"val1": min, 2:[sum, "size"]}).my_flatten_cols(" ".join)
    • a.columns = ["_".join(filter(None, map(str, levels))) for levels in a.columns.values]후드에서 실행 됩니다 (이 형식의 agg()결과가 MultiIndex열에 있기 때문에 ).
    • my_flatten_cols도우미 가 없으면 @Seigi : a.columns = ["_".join(t).rstrip("_") for t in a.columns.values]에서 제안한 솔루션을 입력하는 것이 더 쉬울 수 있습니다 . 이 경우와 비슷하게 작동하지만 열에 숫자 레이블이 있으면 실패합니다.
    • 열의 숫자 레이블을 처리하려면 @jxstanford 및 @Nolan Conaway ( a.columns = ["_".join(tuple(map(str, t))).rstrip("_") for t in a.columns.values])에서 제안한 솔루션을 사용할 수 있지만 tuple()호출이 필요한 이유를 이해하지 못하며 rstrip()일부 열에 ("colname", "")( reset_index()문제를 해결하기 전에 발생할 수 있습니다 .columns)
    • ----- before -----
                 val1           2     
                 min       sum    size
        grouper              
      
      ------ after -----
        grouper  val1_min  2_sum  2_size
      0       x         0      4       2
      1       y         4     12       2
  • 결과 변수의 이름을 수동으로 지정하려고합니다. ( 0.23 기준 으로 적절한 대안없는 팬더 0.20.0부터 더 이상 사용되지 않습니다. )

    df.groupby(by="grouper").agg({"val1": {"sum_of_val1": "sum", "count_of_val1": "count"},
                                       2: {"sum_of_2":    "sum", "count_of_2":    "count"}}).my_flatten_cols("last")
    • 다른 제안으로 열을 수동으로 설정 res.columns = ['A_sum', 'B_sum', 'count']하거나 .join()여러 groupby명령문을 사용하는 것이 있습니다.
    • ----- before -----
                         val1                      2         
                count_of_val1 sum_of_val1 count_of_2 sum_of_2
        grouper                                              
      
      ------ after -----
        grouper  count_of_val1  sum_of_val1  count_of_2  sum_of_2
      0       x              2            2           2         4
      1       y              2           10           2        12

도우미 기능으로 처리 된 사례

  • 열 이름이 정수경우 수준 이름은 문자열이 아닌 수 있습니다 (예 : 열 팬 수에 의한 인덱스 팬더 DataFrame).map(str, ..)
  • 그들은 또한 비어있을 수 있으므로 우리는 filter(None, ..)
  • 단일 수준 열 (예 : MultiIndex를 제외한 모든 열)의 columns.values경우 이름 ( str튜플이 아닌 )을 반환합니다.
  • 사용 방법에 따라 .agg()열의 맨 아래 레이블을 유지하거나 여러 레이블을 연결해야 할 수도 있습니다
  • (팬더를 처음 사용하기 때문에?)보다 자주 reset_index(), 그룹별로 열을 정기적으로 작업 할 수 있기를 원 하므로 기본적으로 그렇게합니다.

정말 좋은 대답 입니다. 사전에 감사합니다 . '[ " ".join (tuple (map (str, t))). rstrip ( " ") in a.columns.values]의 작업에 대해 설명해 주시겠습니까
Vineet

@Vineet 내 게시물을 업데이트하여 스 니펫이 내 솔루션과 유사한 효과를 제안한다고 언급했음을 나타냅니다. 왜 tuple()필요한지 에 대한 세부 정보를 원한다면 jxstanford의 게시물에 댓글을 달 수 있습니다. 그렇지 않으면 .columns.values제공된 예제에서 를 검사하는 것이 도움이 될 수 있습니다 [('val1', 'min'), (2, 'sum'), (2, 'size')]. 1) for t in a.columns.values두 번째 열에 대해 열을 반복합니다 t == (2, 'sum'). 2) 각 "레벨"에 map(str, t)적용 str()되며 ('2', 'sum'); 3) "_".join(('2','sum'))"2_sum"결과
Nickolay

5

여러 수준과 혼합 유형을 처리하는 일반적인 솔루션 :

df.columns = ['_'.join(tuple(map(str, t))) for t in df.columns.values]

1
비 계층 열도있는 경우 :df.columns = ['_'.join(tuple(map(str, t))).rstrip('_') for t in df.columns.values]
Nolan Conaway

감사. 오랫동안 찾고 있었다. 내 다단계 색인에 정수 값이 포함되어 있기 때문에. 그것은 내 문제를 해결 :)
AnksG

4

약간 늦었지만 중복 열 이름이 걱정되지 않는 경우 :

df.columns = df.columns.tolist()

나를 위해,이 튜플 같은 수의 열 '이름을 변경 : (year, )(tempf, amax)
Nickolay

3

레벨 사이의 이름에 구분 기호를 사용하려는 경우이 기능이 제대로 작동합니다.

def flattenHierarchicalCol(col,sep = '_'):
    if not type(col) is tuple:
        return col
    else:
        new_col = ''
        for leveli,level in enumerate(col):
            if not level == '':
                if not leveli == 0:
                    new_col += sep
                new_col += level
        return new_col

df.columns = df.columns.map(flattenHierarchicalCol)

1
나는 그것을 좋아한다. 열이 계층 적이 지 않은 경우를 제외하고는 이것을 단순화 할 수 있습니다.df.columns = ["_".join(filter(None, c)) for c in df.columns]
Gigo

3

@jxstanford 및 @ tvt173에 이어 문자열 / int 열 이름에 관계없이 트릭을 수행하는 빠른 기능을 작성했습니다.

def flatten_cols(df):
    df.columns = [
        '_'.join(tuple(map(str, t))).rstrip('_') 
        for t in df.columns.values
        ]
    return df

1

다음과 같이 할 수도 있습니다. 고려 df(경우에 당신의 예에서와 같이) 귀하의 dataframe로하고,이 개 수준의 인덱스를 가정

df.columns = [(df.columns[i][0])+'_'+(datadf_pos4.columns[i][1]) for i in range(len(df.columns))]

1

나는 나를 위해 일한 간단한 방법을 공유 할 것입니다.

[" ".join([str(elem) for elem in tup]) for tup in df.columns.tolist()]
#df = df.reset_index() if needed

0

다른 DataFrame 메서드 체인 내에서 MultiIndex를 병합하려면 다음과 같이 함수를 정의하십시오.

def flatten_index(df):
  df_copy = df.copy()
  df_copy.columns = ['_'.join(col).rstrip('_') for col in df_copy.columns.values]
  return df_copy.reset_index()

이어서 사용 pipe방법을 후 DataFrame 방법 체인에이 기능을 적용 groupby하고 agg있지만, 사슬에서 다른 방법 전 :

my_df \
  .groupby('group') \
  .agg({'value': ['count']}) \
  .pipe(flatten_index) \
  .sort_values('value_count')

0

또 다른 간단한 루틴.

def flatten_columns(df, sep='.'):
    def _remove_empty(column_name):
        return tuple(element for element in column_name if element)
    def _join(column_name):
        return sep.join(column_name)

    new_columns = [_join(_remove_empty(column)) for column in df.columns.values]
    df.columns = new_columns
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.