Python pandas groupby条件将字符串连接成多个列

时间:2016-11-10 03:54:04

标签: python pandas group-by conditional string-concatenation

我正在尝试按一个列上的数据框进行分组,从每个组中的一行保留多个列,并根据一列的值将其他行的字符串连接成多个列。这是一个例子......

df = pd.DataFrame({'test' : ['a','a','a','a','a','a','b','b','b','b'],
     'name' : ['aa','ab','ac','ad','ae','ba','bb','bc','bd','be'],
     'amount' : [1, 2, 3, 4, 5, 6, 7, 8, 9, 9.5],
     'role' : ['x','y','y','x','x','z','y','y','z','y']})

DF

      amount    name    role    test
0        1.0    aa      x       a
1        2.0    ab      y       a
2        3.0    ac      y       a
3        4.0    ad      x       a
4        5.0    ae      x       a
5        6.0    ba      z       a
6        7.0    bb      y       b
7        8.0    bc      y       b
8        9.0    bd      z       b
9        9.5    be      y       b

我想分组测试,在role ='z'时保留名称和数量,创建一个列(让我们称之为X),当role ='x'和另一列时,我们将名称的值连接起来(让我们称之为Y)当role ='y'时连接name的值。 [以';分隔的连接值; ']可能有零到多行,其中role ='x',零到多行,其中role ='y',一行的role ='z',每个test值。对于X和Y,如果该测试的该角色没有行,则这些可以为null。对于role ='x'或'y'的所有行,将删除金额值。所需的输出类似于:

     test   name     amount        X              Y
0    a      ba          6.0        aa; ad; ae     ab; ac
1    b      bd          9.0        None           bb; bc; be

对于连接部分,我找到x.ix[x.role == 'x', X] = "{%s}" % '; '.join(x['name']),我可以重复y。我尝试了一些name = x[x.role == 'z'].name.first()的名称和金额。我也尝试了定义函数和lambda函数的两个路径,但没有成功。感谢任何想法。

2 个答案:

答案 0 :(得分:1)

您可以在apply之后的groupby函数中创建自定义列,如下所示g可以被视为测试列中具有单个值的子数据框,并且因为您需要返回多个列,您需要为每个组创建一个Series对象,其中索引是结果中的相应标题:

df.groupby('test').apply(lambda g: pd.Series({'name': g['name'][g.role == 'z'].iloc[0],
                                              'amount': g['amount'][g.role == 'z'].iloc[0], 
                                              'X': '; '.join(g['name'][g.role == 'x']), 
                                              'Y': '; '.join(g['name'][g.role == 'y'])
                                             })).reset_index()

enter image description here

答案 1 :(得分:1)

# set index and get crossection where test is 'z'
z = df.set_index(['test', 'role']).xs('z', level='role')
# get rid of 'z' rows and group by 'test' and 'role' to join names
xy = df.query('role != "z"').groupby(['test', 'role'])['name'].apply(';'.join).unstack()
# make columns of xy upper case
xy.columns = xy.columns.str.upper()

pd.concat([z, xy], axis=1).reset_index()

enter image description here