Python 熊猫-如何展平列中的层次索引

Python 熊猫-如何展平列中的层次索引,python,pandas,dataframe,Python,Pandas,Dataframe,我有一个在轴1(列)中具有层次索引的数据帧(来自groupby.agg操作): 我想将其展平,使其看起来像这样(名称不重要-我可以重命名): 我该怎么做?(我试了很多,但都没有用。) 根据建议,这是dict格式的头部 {('USAF', ''): {0: '702730', 1: '702730', 2: '702730', 3: '702730', 4: '702730'}, ('WBAN', ''): {0: '26451', 1: '26451', 2: '26451',

我有一个在轴1(列)中具有层次索引的数据帧(来自
groupby.agg
操作):

我想将其展平,使其看起来像这样(名称不重要-我可以重命名):

我该怎么做?(我试了很多,但都没有用。)

根据建议,这是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}}

我认为最简单的方法是将列设置为顶层:

df.columns = df.columns.get_level_values(0)
注意:如果to级别有名称,您也可以通过该名称而不是0来访问它

如果要将多索引合并到一个索引中(假设列中只有字符串项),可以:

df.columns = [' '.join(col).strip() for col in df.columns.values]
注意:当没有第二个索引时,必须使用空格


如果要保留多索引第二级的任何聚合信息,可以尝试以下方法:

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

安迪·海登的答案当然是最简单的方法——如果你想避免重复的列标签,你需要稍微调整一下

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

如果希望在级别之间的名称中使用分隔符,则此函数可以正常工作

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)

您也可以按以下操作。考虑<代码> df>代码>作为您的数据文件,并假设一个两级索引(如您的示例中的情况)


可能有点晚,但如果您不担心重复的列名:

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

处理多个级别和混合类型的通用解决方案:

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

在@jxstanford和@tvt173之后,我编写了一个快速函数,不管string/int列名如何,它都应该能够完成这一任务:

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

在阅读了所有答案后,我得出了以下结论:

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").agg({"val1": [min,max]}).my_flatten_cols("last")
    
    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)
    
    • df.groupby(by=“grouper”,
      相同,因为_index=False
      .agg(…)
      .reset_index()
  • 单源变量,多个聚合:结果变量以统计数据命名

    df.groupby(by="grouper").agg("min").my_flatten_cols()
    
    df.groupby(by="grouper").agg({"val1": [min,max]}).my_flatten_cols("last")
    
    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=df.groupby(..).agg(..)相同;a、 columns=a.columns.droplevel(0);a、 重置索引()
  • 多个变量,多个聚合:结果变量命名为(varname)\uu(statname)

    df.groupby(by="grouper").agg("min").my_flatten_cols()
    
    df.groupby(by="grouper").agg({"val1": [min,max]}).my_flatten_cols("last")
    
    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.values中的级别运行
      a.columns=[“979;”.join(过滤器(无,映射(str,levels))
      (因为这种形式的
      agg()
      导致列上的
      多索引)
    • 如果您没有
      my_flatten\u cols
      帮助程序,那么键入以下建议的解决方案可能会更容易:
      a.columns=[“277;”.join(t).rstrip(“277;”)for t in a.columns.values]
      ,这在这种情况下也会起到类似的作用(但如果列上有数字标签,则会失败)
    • 要处理列上的数字标签,您可以使用(
      a.columns=[“979;”.join(tuple(map(str,t))).rstrip(“979;”)建议的解决方案来处理a.columns.values中的t]
      ),但我不理解为什么需要调用
      tuple()
      ,我相信只有在某些列具有类似
      的描述符时才需要
      rstrip()
      (“colname”,”)
      (如果在尝试修复
      列之前
      重置索引()
      ,则可能发生这种情况)
  • 您想手动命名结果变量:(这是随附的)

    • 建议:手动设置列:
      res.columns=['A\u sum',B\u sum',count']
      .join()
      ing多个
      groupby
      语句
由helper函数处理的案例
  • 级别名称可以是非字符串,例如,因此我们必须使用
    map(str,…)
  • 它们也可以是空的,因此我们必须
    筛选(无,…)
  • 对于单级列(即除多索引外的任何列),
    columns.values
    返回名称(
    str
    ,而不是元组)
  • 根据您使用
    .agg()
    的方式,您可能需要保留列的最底部标签或连接多个标签
  • (因为我是pandas的新手?)

    • 使用
      map
      函数是实现这一点的最具python风格的方法

      df.columns = df.columns.map(' '.join).str.strip()
      
      输出
      打印(测向列)

      使用Python 3.6+和f字符串进行更新: 输出:

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

      我将分享一种对我有效的直截了当的方法

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

      要展平其他数据帧方法链中的多索引,请定义如下函数:

      def展平指数(df):
      df_copy=df.copy()
      df_copy.columns=['''''.'连接(col).rstrip(''''.'用于df_copy.columns.values中的col]
      返回df_copy.reset_index()
      
      然后使用在数据帧方法链中应用此函数,在
      groupby
      agg
      之后,但在链中的任何其他方法之前:

      my_df\
      .groupby(“组”)\
      .agg({'value':['count']})\
      .管道(展平指数)\
      .sort_值('value_count')
      
      此线程上的所有当前答案都必须有点过时。从
      pandas
      版本0.24.0开始,
      .to\u flat\u 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'])
      
      应用于平面索引()


      使用它替换现有的
      列
      关于如何在
      dat
      上使用它的示例,dat是一个带有
      多索引列的数据帧:

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

      就地展开和重命名 可能值得注意的是,您如何将其与简单的列表理解相结合(感谢@Skippy和@mmann1123),以加入元素,从而使生成的列名变得简单
      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')
      
      [" ".join([str(elem) for elem in tup]) for tup in df.columns.tolist()]
      #df = df.reset_index() if needed
      
      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'])
      
      index.to_flat_index()
      # Index([('foo', 'baz'), ('foo', 'qux'), ('bar', 'baz'), ('bar', 'qux')], dtype='object')
      
      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')
      
      dat.columns = ["_".join(a) for a in dat.columns.to_flat_index()]
      
      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
      
      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
      
      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
      
      df.columns = df.columns.to_flat_index().str.join('_')
      
          USAF_  WBAN_  day_  month_  ...  s_PC_sum  tempf_amax  tempf_amin  year_
      0  702730  26451     1       1  ...       1.0       30.92       24.98   1993
      1  702730  26451     2       1  ...       0.0       32.00       24.98   1993
      2  702730  26451     3       1  ...       1.0       23.00        6.98   1993
      3  702730  26451     4       1  ...       1.0       10.04        3.92   1993
      4  702730  26451     5       1  ...       3.0       19.94       10.94   1993