【问题标题】:pandas groupby: building multiple columns efficientlypandas groupby:有效地构建多个列
【发布时间】:2016-11-10 03:30:00
【问题描述】:

我有一个带有多索引的数据框,这样我就可以轻松地创建一个使用来自多列的数据作为输入的函数:

df = pd.DataFrame({('ALSN','VA.M'):range(5), ('ALSN','VB.M'):np.arange(5)+2,
                  ('ALVY','VA.M'):range(5), ('ALVY','VB.M'):np.arange(5)+20,
                  ('ALSN', 'VP.M'):np.arange(5)-10, ('ALVY','VP.M'):np.arange(5)-30,
                  ('ALGG', 'VP.M'):np.arange(5)/5.})

        ALGG    ALSN                    ALVY
        VP.M    VA.M    VB.M    VP.M    VA.M    VB.M    VP.M
0       0.0     0       2       -10     0       20      -30
1       0.2     1       3       -9      1       21      -29
2       0.4     2       4       -8      2       22      -28
3       0.6     3       5       -7      3       23      -27
4       0.8     4       6       -6      4       24      -26

我想对此进行过滤,然后将一个函数应用于 VA.M 和 VB.M 列:

df2 = g.filter(lambda z: z.name[-1] != 'G')
df2.groupby(level=0, axis=1).apply(lambda g: pd.Series(g[g.name]['VB.M']+g[g.name]['VA.M']))

    ALSN    ALVY
0   2       20
1   4       22
2   6       24
3   8       26
4   10      28

所以,到目前为止一切顺利。 但是,我真正想做的是构建一个函数,该函数将多列作为输入(如上),然后输出多列。因此,例如,它可以对 VA.M 和 VB.M 列求和,然后将平方根和立方根作为新列返回。

显然,我可以使用两个不同的应用函数(计算总和,然后计算平方根或立方根)来完成此操作,但我只想执行一次中间步骤(计算总和)。这可能吗?

此外,我希望将输出存储回数据框中,例如,列 ('ALSN', 'V2') 和 ('ALSN', 'V3') 可以在同时申请?还是我需要计算 V2 或 V3 列,然后获取结果数据帧并与原始数据帧合并?

【问题讨论】:

    标签: python pandas


    【解决方案1】:

    这并不容易。

    首先通过get_level_valuesboolean indexing 找到MultiIndex 的第一级值,然后通过slicers 选择:

    lvl = df.columns.get_level_values(0).unique()
    lvl = lvl[~lvl.str.contains('G')]
    print (lvl)
    Index(['ALSN', 'ALVY'], dtype='object')
    
    idx = pd.IndexSlice
    df2 = df.loc[:, idx[lvl, ['VA.M','VB.M']]]
    print (df2)
      ALSN      ALVY     
      VA.M VB.M VA.M VB.M
    0    0    2    0   20
    1    1    3    1   21
    2    2    4    2   22
    3    3    5    3   23
    4    4    6    4   24
    

    对每个函数使用groupby,然后使用concatunstack

    print (pd.concat([df2.groupby(level=0, axis=1).apply(lambda x: (x**2).sum(axis=1)), 
                      df2.groupby(level=0, axis=1).apply(lambda x: (x**3).sum(axis=1))],
                      keys=('x^2','x^3')).unstack(0))
    
      ALSN      ALVY       
       x^2  x^3  x^2    x^3
    0    4    8  400   8000
    1   10   28  442   9262
    2   20   72  488  10656
    3   34  152  538  12194
    4   52  280  592  13888
    

    另一个非常相似的解决方案:

    print (df2.groupby(level=0, axis=1).apply(lambda x: (x**2)))
      ALSN      ALVY     
      VA.M VB.M VA.M VB.M
    0    0    4    0  400
    1    1    9    1  441
    2    4   16    4  484
    3    9   25    9  529
    4   16   36   16  576
    
    print (df2.groupby(level=0, axis=1).apply(lambda x: (x**3)))
      ALSN      ALVY       
      VA.M VB.M VA.M   VB.M
    0    0    8    0   8000
    1    1   27    1   9261
    2    8   64    8  10648
    3   27  125   27  12167
    4   64  216   64  13824
    
    df21 = df2.groupby(level=0, axis=1).apply(lambda x: (x**2).sum(axis=1))
    df22 = df2.groupby(level=0, axis=1).apply(lambda x: (x**3).sum(axis=1))
    print (df21)
       ALSN  ALVY
    0     4   400
    1    10   442
    2    20   488
    3    34   538
    4    52   592
    
    print (df22)
       ALSN   ALVY
    0     8   8000
    1    28   9262
    2    72  10656
    3   152  12194
    4   280  13888
    
    print (pd.concat([df21,df22], keys=('x^2','x^3')).unstack(0))
      ALSN      ALVY       
       x^2  x^3  x^2    x^3
    0    4    8  400   8000
    1   10   28  442   9262
    2   20   72  488  10656
    3   34  152  538  12194
    4   52  280  592  13888
    

    通过评论编辑:

    df = df2.groupby(level=0, axis=1).sum()
    print (df)
       ALSN  ALVY
    0     2    20
    1     4    22
    2     6    24
    3     8    26
    4    10    28
    
    df3 = pd.concat([df ** 2, df ** 3], keys=('x^2','x^3'), axis=1)
    df3.columns = df3.columns.swaplevel(0,1)
    df3 = df3.sort_index(axis=1)
    print (df3)
      ALSN       ALVY       
       x^2   x^3  x^2    x^3
    0    4     8  400   8000
    1   16    64  484  10648
    2   36   216  576  13824
    3   64   512  676  17576
    4  100  1000  784  21952
    

    【讨论】:

    • 尽量不要使用非常复杂的apply;用户会认为这是做事的正确方式; iow 你的 sol 2 要好得多 - 如果不直接在组上应用和使用 .sum() 可能会更好(有点棘手但可能)
    • @Jeff - 谢谢,我试试。我删除第一个解决方案。
    • @jezrael -- 感谢您的建议!两个问题:(1)你使用的是什么版本的熊猫/numpy?我对第 2 行中的 .str 不满意: ~lvl.str.contains('G') ; 2)在您的两个解决方案中,您在求和之前都应用了x**2x**3。我想要做的是先执行总和,然后执行下一个操作,理想情况下我不想计算总和一次(不是在x**2 之前一次,然后在x**3 之前再次计算)
    • @SWallace 我使用的是最新版本的 pandas - 0.19.1
    • @jezrael 感谢您的建议,我自己不会想出这个。这当然不容易。有趣的是,当我在具有约 2000 列的数据帧上使用多索引并应用此方法生成约 200 条新数据列时,简单地迭代非分层列大约快 15%,而不是使用 groupby 进行选择。这让我很惊讶。
    猜你喜欢
    • 2018-08-20
    • 2020-04-16
    • 2019-07-02
    • 2017-03-09
    • 1970-01-01
    • 2022-11-23
    • 2015-10-15
    • 1970-01-01
    • 2017-06-15
    相关资源
    最近更新 更多