首页 文章

将行附加到pandas中的组

提问于
浏览
3

我正在尝试在pandas数据帧中为每个组添加一些NaN行 . 基本上我想将每组填充为5行长 . 订购很重要 . 我有:

Rank id
0   1  a
1   2  a
2   3  a
3   4  a
4   5  a
5   1  c
6   2  c
7   1  e
8   2  e
9   3  e

我想要:

Rank id
0   1    a
1   2    a
2   3    a
3   4    a
4   5    a
5   1    c
6   2    c
7   NaN  c
8   NaN  c
9   NaN  c
10  1    e
11  2    e
12  3    e
13  NaN  e
14  NaN  e

5 回答

  • 4

    使用 pd.crosstab

    df = pd.crosstab(df.Rank, df.ID).iloc[:5].unstack().reset_index()
    df.loc[(df[0]==0),'Rank'] = np.nan
    del df[0]
    

    输出:

    ID  Rank
    0   a   1.0
    1   a   2.0
    2   a   3.0
    3   a   4.0
    4   a   5.0
    5   c   1.0
    6   c   2.0
    7   c   NaN
    8   c   NaN
    9   c   NaN
    10  e   1.0
    11  e   2.0
    12  e   3.0
    13  e   NaN
    14  e   NaN
    

    另一种方法,假设 df 中的最大组大小正好为5 .

    In [251]: df.groupby('ID').Rank.apply(np.array).apply(pd.Series).stack(dropna=False)
    Out[251]: 
    ID
    a   0    1.0
        1    2.0
        2    3.0
        3    4.0
        4    5.0
    c   0    1.0
        1    2.0
        2    NaN
        3    NaN
        4    NaN
    e   0    1.0
        1    2.0
        2    3.0
        3    NaN
        4    NaN
    dtype: float64
    

    完整说明:

    import pandas as pd
    import numpy as np
    
    df = pd.read_csv(pd.compat.StringIO("""Rank ID
    0   1  a
    1   2  a
    2   3  a
    3   4  a
    4   5  a
    6   1  c
    7   2  c
    8   1  e
    9   2  e
    10  3  e"""), sep=r' +')
    
    df = pd.crosstab(df.Rank, df.ID).iloc[:5].T.stack().reset_index()
    df.loc[(df[0]==0),'Rank'] = np.nan
    del df[0]
    
    # pd.crosstab(df.Rank, df.ID) produces:
    
    # ID    a  c  e
    # Rank
    # 1.0   1  1  1
    # 2.0   1  1  1
    # 3.0   1  0  1
    # 4.0   1  0  0
    # 5.0   1  0  0
    
    # applying .T.stack().reset_index() yields:
    
       # ID  Rank  0
    # 0   a   1.0  1
    # 1   a   2.0  1
    # 2   a   3.0  1
    # 3   a   4.0  1
    # 4   a   5.0  1
    # 5   c   1.0  1
    # 6   c   2.0  1
    # 7   c   3.0  0
    # 8   c   4.0  0
    # 9   c   5.0  0
    # 10  e   1.0  1
    # 11  e   2.0  1
    # 12  e   3.0  1
    # 13  e   4.0  0
    # 14  e   5.0  0
    
    # finally, use df[0] to filter df['Rank']
    
  • 1

    concat和reindex

    此解决方案不考虑 Rank 列中的值,仅在需要更多行时才添加更多行 .

    pd.concat([
        d.reset_index(drop=True).reindex(range(5)).assign(id=n)
        for n, d in df.groupby('id')
    ], ignore_index=True)
    
        Rank id
    0    1.0  a
    1    2.0  a
    2    3.0  a
    3    4.0  a
    4    5.0  a
    5    1.0  c
    6    2.0  c
    7    NaN  c
    8    NaN  c
    9    NaN  c
    10   1.0  e
    11   2.0  e
    12   3.0  e
    13   NaN  e
    14   NaN  e
    

    同样的答案措辞有点不同

    f = lambda t: t[1].reset_index(drop=True).reindex(range(5)).assign(id=t[0])
    pd.concat(map(f, df.groupby('id')), ignore_index=True)
    

    factorize

    此解决方案生成独特值的笛卡尔积 idRank

    i, r = df.id.factorize()
    j, c = df.Rank.factorize()
    b = np.empty((r.size, c.size))
    b.fill(np.nan)
    b[i, j] = df.Rank.values
    
    pd.DataFrame(dict(Rank=b.ravel(), id=r.repeat(c.size)))
    
        Rank id
    0    1.0  a
    1    2.0  a
    2    3.0  a
    3    4.0  a
    4    5.0  a
    5    1.0  c
    6    2.0  c
    7    NaN  c
    8    NaN  c
    9    NaN  c
    10   1.0  e
    11   2.0  e
    12   3.0  e
    13   NaN  e
    14   NaN  e
    
  • 4

    您可以使用id和 pd.concat 的频率来合并重复,即

    di = (5-df.groupby('id').size()).to_dict()
    
    temp = pd.concat([pd.DataFrame({
                    'Rank':np.nan,
                    'id': pd.Series(np.repeat(i,di[i]))
                    }) for i in df['id'].unique()])
    
    ndf = pd.concat([df,temp],ignore_index=True).sort_values('id')
    
        Rank id
    0    1.0  a
    1    2.0  a
    2    3.0  a
    3    4.0  a
    4    5.0  a
    5    1.0  c
    6    2.0  c
    10   NaN  c
    11   NaN  c
    12   NaN  c
    7    1.0  e
    8    2.0  e
    9    3.0  e
    13   NaN  e
    14   NaN  e
    
  • 3

    一种可能的解决方案是创建助手 DataFramenumpy.repeat然后 append 到原始,最后sort_values

    s = (5 - df['id'].value_counts())
    df = (df.append(pd.DataFrame({'id':np.repeat(s.index, s.values), 'Rank':np.nan}))
           .sort_values('id')
           .reset_index(drop=True))
    print (df)
        Rank id
    0    1.0  a
    1    2.0  a
    2    3.0  a
    3    4.0  a
    4    5.0  a
    5    1.0  c
    6    2.0  c
    7    NaN  c
    8    NaN  c
    9    NaN  c
    10   1.0  e
    11   2.0  e
    12   3.0  e
    13   NaN  e
    14   NaN  e
    

    另一个解决方案是没有可能的排序是groupby与自定义功能和 append

    def f(x):
        return x.append(pd.DataFrame([[np.nan, x.name]] * (5 - len(x)), columns=['Rank','id']))
    df = df.groupby('id', sort=False).apply(f).reset_index(drop=True)
    print (df)
       Rank id
    0     1  a
    1     2  a
    2     3  a
    3     4  a
    4     5  a
    5     1  c
    6     2  c
    7   NaN  c
    8   NaN  c
    9   NaN  c
    10    1  e
    11    2  e
    12    3  e
    13  NaN  e
    14  NaN  e
    
  • 0

    这是使用单个 pd.DataFrame.append 跟随 sort_values 的一种方式 .

    from itertools import chain
    
    counts = df.groupby('id')['Rank'].count()
    
    lst = list(chain.from_iterable([[np.nan, i]]*(5-c) for i, c in counts.items()))
    
    res = df.append(pd.DataFrame(lst, columns=df.columns))\
            .sort_values(['id', 'Rank'])\
            .reset_index(drop=True)
    
    print(res)
    
        Rank id
    0    1.0  a
    1    2.0  a
    2    3.0  a
    3    4.0  a
    4    5.0  a
    5    1.0  c
    6    2.0  c
    7    NaN  c
    8    NaN  c
    9    NaN  c
    10   1.0  e
    11   2.0  e
    12   3.0  e
    13   NaN  e
    14   NaN  e
    

相关问题