代码之家  ›  专栏  ›  技术社区  ›  yatu Sayali Sonawane

每列不同的石斑鱼和熊猫石斑鱼

  •  7
  • yatu Sayali Sonawane  · 技术社区  · 6 年前

    如何使用多维Grouper(在本例中是另一个数据帧)作为另一个数据帧的Grouper?可以一步完成吗?

    我的问题本质上是关于如何在这些情况下执行实际分组,但为了使其更具体,我想这样做 transform sum .

    例如:

    df1 = pd.DataFrame({'a':[1,2,3,4], 'b':[5,6,7,8]})
    
    print(df1)
       a  b
    0  1  5
    1  2  6
    2  3  7
    3  4  8
    
    df2  = pd.DataFrame({'a':['A','B','A','B'], 'b':['A','A','B','B']})
    
    print(df2)
       a  b
    0  A  A
    1  B  A
    2  A  B
    3  B  B
    

       a  b
    0  4  11
    1  6  11
    2  4  15
    3  6  15
    

    Where列 a b 在里面 df1 已按列分组 A. B df2 分别地

    5 回复  |  直到 6 年前
        1
  •  7
  •   Scott Boston    6 年前

    试用 apply

    df1.apply(lambda x: x.groupby(df2[x.name]).transform('sum'))
    

       a   b
    0  4  11
    1  6  11
    2  4  15
    3  6  15
    
        2
  •  7
  •   cs95 abhishek58g    6 年前

    由于每列使用不同的分组方案,因此必须单独对每列进行分组。

    如果你想要一个更简洁的版本,我建议你在列名上加一个列表,然后调用 pd.concat 关于结果系列:

    pd.concat([df1[c].groupby(df2[c]).transform('sum') for c in df1.columns], axis=1)
    
       a   b
    0  4  11
    1  6  11
    2  4  15
    3  6  15
    

    不用说使用它有什么问题 apply 就像另一个答案一样,只是我不喜欢 申请


    这里有一些时间供你阅读。就您的示例数据而言,您会注意到计时的差异是显而易见的。

    %%timeit 
    (df1.stack()
        .groupby([df2.stack().index.get_level_values(level=1), df2.stack()])
        .transform('sum').unstack())
    %%timeit 
    df1.apply(lambda x: x.groupby(df2[x.name]).transform('sum'))
    %%timeit 
    pd.concat([df1[c].groupby(df2[c]).transform('sum') for c in df1.columns], axis=1)
    
    8.99 ms ± 4.55 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
    8.35 ms ± 859 µs per loop (mean ± std. dev. of 7 runs, 1 loop each)
    6.13 ms ± 279 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
    

    不用说 速度很慢,但在这种情况下显式迭代速度更快。此外,您会注意到第二个和第三个计时解决方案将更好地扩展为更大的长度v/s宽度,因为迭代次数取决于列的数量。

        3
  •  4
  •   BENY    6 年前

    stack unstack

    df1.stack().groupby([df2.stack().index.get_level_values(level=1),df2.stack()]).transform('sum').unstack()
    Out[291]: 
       a   b
    0  4  11
    1  6  11
    2  4  15
    3  6  15
    
        4
  •  2
  •   user3483203    6 年前

    scipy.sparse_matrix 执行矢量化 groupby 一次在整个数据帧上,而不是逐列。


    每个值末尾的名称(从列开始)

    >>> df2 + df2.columns
        a   b
    0  Aa  Ab
    1  Ba  Ab
    2  Aa  Bb
    3  Ba  Bb
    
    >>> pd.factorize((df2 + df2.columns).values.ravel())
    (array([0, 1, 2, 1, 0, 3, 2, 3], dtype=int64),
    array(['Aa', 'Ab', 'Ba', 'Bb'], dtype=object))
    

    scipy.sparse

    from scipy import sparse
    
    a = df1.values.ravel()
    b, _ = pd.factorize((df2 + df2.columns).values.ravel())
    
    o = sparse.csr_matrix(
        (a, b, np.arange(a.shape[0] + 1)), (a.shape[0], b.max() + 1)
    ).sum(0).A1
    
    res = o[b].reshape(df1.shape)
    

    array([[ 4, 11],
           [ 6, 11],
           [ 4, 15],
           [ 6, 15]], dtype=int64)
    

    表演

    def gp_chris(f1, f2):
        a = f1.values.ravel()
        b, _ = pd.factorize((f2 + f2.columns).values.ravel())
    
        o = sparse.csr_matrix(
            (a, b, np.arange(a.shape[0] + 1)), (a.shape[0], b.max() + 1)
        ).sum(0).A1
    
        return pd.DataFrame(o[b].reshape(f1.shape), columns=df1.columns)
    
    
    def gp_cs(f1, f2):
        return pd.concat([f1[c].groupby(f2[c]).transform('sum') for c in f1.columns], axis=1)
    
    
    def gp_scott(f1, f2):
        return f1.apply(lambda x: x.groupby(f2[x.name]).transform('sum'))
    
    
    def gp_wen(f1, f2):
        return f1.stack().groupby([f2.stack().index.get_level_values(level=1), f2.stack()]).transform('sum').unstack()
    

    安装程序

    import numpy as np
    from scipy import sparse
    import pandas as pd
    import string
    from timeit import timeit
    import matplotlib.pyplot as plt
    res = pd.DataFrame(
           index=[f'gp_{f}' for f in ('chris', 'cs', 'scott', 'wen')],
           columns=[10, 50, 100, 200, 400],
           dtype=float
    )
    
    for f in res.index:
        for c in res.columns:
            df1 = pd.DataFrame(np.random.rand(c, c))
            df2 = pd.DataFrame(np.random.choice(list(string.ascii_uppercase), (c, c)))
            df1.columns = df1.columns.astype(str)
            df2.columns = df2.columns.astype(str)
    
            stmt = '{}(df1, df2)'.format(f)
            setp = 'from __main__ import df1, df2, {}'.format(f)
            res.at[f, c] = timeit(stmt, setp, number=50)
    
    
    ax = res.div(res.min()).T.plot(loglog=True)
    ax.set_xlabel("N")
    ax.set_ylabel("time (relative)")
    
    plt.show()
    

    enter image description here


    验证

    df1 = pd.DataFrame(np.random.rand(10, 10))
    df2 = pd.DataFrame(np.random.choice(list(string.ascii_uppercase), (10, 10)))
    df1.columns = df1.columns.astype(str)
    df2.columns = df2.columns.astype(str)
    
    v = np.stack([gp_chris(df1, df2), gp_cs(df1, df2), gp_scott(df1, df2), gp_wen(df1, df2)])
    print(np.all(v[:-1] == v[1:]))
    

    True
    

    要么我们都错了,要么我们都对:)


    如果一项是列和另一项在连接之前的连接,则有可能在此处获得重复值。然而,如果是这种情况,你不需要调整太多来修复它。

        5
  •  0
  •   PMende    6 年前

    您可以执行以下操作:

    res = df1.assign(a_sum=lambda df: df['a'].groupby(df2['a']).transform('sum'))\
             .assign(b_sum=lambda df: df['b'].groupby(df2['b']).transform('sum'))
    

    结果:

       a   b
    0  4  11
    1  6  11
    2  4  15
    3  6  15