pandas groupby当一条记录属于多个组

时间:2016-05-20 15:00:22

标签: python database pandas

我希望能够从可以多种方式分组的数据集中生成摘要统计信息和数据透视表。由于每个条目可以属于一个分类轴内的多个组,因此会出现复杂情况(参见下面的示例)。

到目前为止,我已经找到了一种基于多索引的解决方案,并且重复每个记录的次数与category1 * category2组合中出现的次数一样多。但是,这似乎不灵活(我需要检查条目是否出现在不同数据源的相同类别中,我可能想要添加另一个类别系统,称为category3,类别“d”可能会被添加到category1系统等)。而且,它似乎违背了数据库设计的基本原则。

我的问题是:除了我的解决方案之外,还有其他(更优雅,更灵活)解决此问题的方法吗?我可以设想保存各种表,一个包含实际数据,另一个包含分组信息(很像下面的堆栈表)并灵活地使用它们作为Groupby的输入,但我不知道这是否可行以及如何制作那工作。任何改进建议也欢迎。谢谢!

原始数据如下所示:

import pandas

data={'ID' : [1 , 2, 3, 4],
    'year' : [2004, 2008 , 2006, 2009],
      'money' : [10000 , 5000, 4000, 11500],
      'categories1' : [ "a,b,c" , "c" , "a,c" , ""  ],
     'categories2' : ["one, two" , "one" , "five" , "eight"]}
df= pandas.DataFrame(data)
df.set_index('ID', inplace=True)
print df

给出了:

   categories1 categories2  money  year
ID                                     
1        a,b,c    one, two  10000  2004
2            c         one   5000  2008
3          a,c        five   4000  2006
4                    eight  11500  2009

我希望能够制作如下所示的数据透视表:

Average money
year      2004   2005   2006   2007
category  
a         
b
c

还有:

Average money
category2      one  two   three    four   
category1  
a         
b
c

到目前为止,我有:

步骤1:使用get_dummies提取类别信息:

cat1=df['categories1'].str.get_dummies(sep=",")
print cat1

给出了:

    a  b  c
ID         
1   1  1  1
2   0  0  1
3   1  0  1
4   0  0  0

第2步:堆叠:

stack = cat1.stack()
stack.index.names=['ID', 'cat1']
stack.name='in_cat1'
​print stack

给出了:

ID  cat1
1   a       1
    b       1
    c       1
2   a       0
    b       0
    c       1
3   a       1
    b       0
    c       1
4   a       0
    b       0
    c       0
Name: in_cat1, dtype: int64

步骤3:将其连接到原始数据框以创建多索引数据框

dl = df.join(stack, how='inner')
print dl

看起来像这样:

        categories1 categories2  money  year  in_cat1
ID cat1                                              
1  a          a,b,c    one, two  10000  2004        1
   b          a,b,c    one, two  10000  2004        1
   c          a,b,c    one, two  10000  2004        1
2  a              c         one   5000  2008        0
   b              c         one   5000  2008        0
   c              c         one   5000  2008        1
3  a            a,c        five   4000  2006        1
   b            a,c        five   4000  2006        0
   c            a,c        five   4000  2006        1
4  a                      eight  11500  2009        0
   b                      eight  11500  2009        0
   c                      eight  11500  2009        0

步骤4:然后可以使用pandas groupby和pivot_table命令

dl.reset_index(level=1, inplace=True)
pt= dl.pivot_table(values='money', columns='year', index='cat1')
print pt

并做我想做的事:

year   2004  2006  2008   2009
cat1                          
a     10000  4000  5000  11500
b     10000  4000  5000  11500
c     10000  4000  5000  11500

我已经使用category2重复了步骤2 + 3,因此现在数据帧具有3级索引。

1 个答案:

答案 0 :(得分:0)

我创建了一个带有DataFrame和列名的函数。预计列名指定的列具有可以由','拆分的字符串。它会将此拆分附加到具有适当名称的索引。

def expand_and_add(df, col):
    expand = lambda x: pd.concat([x for i in x[col].split(',')], keys=x[col].split(','))
    df = df.apply(expand, axis=1).stack(0)
    df.index.levels[-1].name = col
    df.drop(col, axis=1, inplace=1)
    return df

现在这将有助于创建3层MultiIndex。我相信操纵MultiIndex提供了创建所需枢轴所需的所有灵活性。

new_df = expand_and_add(expand_and_add(df, 'categories1'), 'categories2')

看起来像:

money    year
ID categories1 categories2                 
1  a            two         10000.0  2004.0
                one          10000.0  2004.0
   b            two         10000.0  2004.0
                one          10000.0  2004.0
   c            two         10000.0  2004.0
                one          10000.0  2004.0
2  c           one           5000.0  2008.0
3  a           five          4000.0  2006.0
   c           five          4000.0  2006.0
4              eight        11500.0  2009.0

你的枢轴仍然会个别凌乱,但这里有一些。

表示[categories1,year]

new_df.set_index(ndf.year.astype(int), append=True)['money'].groupby(level=[1, 3]).mean().unstack()

year            2004    2006    2008     2009
categories1                                  
                 NaN     NaN     NaN  11500.0
a            10000.0  4000.0     NaN      NaN
b            10000.0     NaN     NaN      NaN
c            10000.0  4000.0  5000.0      NaN

表示[categories1,categories2]

new_df.groupby(level=[1, 2])['money'].mean().unstack()

categories2      two    eight    five      one
categories1                                   
                 NaN  11500.0     NaN      NaN
a            10000.0      NaN  4000.0  10000.0
b            10000.0      NaN     NaN  10000.0
c            10000.0      NaN  4000.0   7500.0