代码之家  ›  专栏  ›  技术社区  ›  BENY

如何取消(分解)数据帧中的列?

  •  40
  • BENY  · 技术社区  · 7 年前

    我有以下DataFrame,其中一列是对象(列表类型单元格):

    df=pd.DataFrame({'A':[1,2],'B':[[1,2],[1,2]]})
    df
    Out[458]: 
       A       B
    0  1  [1, 2]
    1  2  [1, 2]
    

       A  B
    0  1  1
    1  1  2
    3  2  1
    4  2  2
    

    我应该怎么做才能做到这一点?


    相关问题

    pandas: When cell contents are lists, create a row for each element in the list

    很好的问题和答案,但只处理一列与列表(在我的答案自我定义功能将为多个列工作,而且接受的答案是使用最耗时的 apply ,这是不推荐的,请查看更多信息 When should I ever want to use pandas apply() in my code?

    5 回复  |  直到 7 年前
        1
  •  0
  •   Golden Lion    5 年前

    我知道 object type 使数据很难用 pandas

    我正在使用 熊猫 python answer ,因为它正在使用 numpy 大多数时候 努比 速度更快。我推荐 Cpython numba 如果速度很重要的话。


    方法0[pandas>=0.25]
    从开始 pandas 0.25 ,如果你只需要爆炸 pandas.DataFrame.explode

    df.explode('B')
    
           A  B
        0  1  1
        1  1  2
        0  2  1
        1  2  2
    

    给定一个数据帧 list NaN 在列中。空列表不会引起问题,但是 列表

    df = pd.DataFrame({'A': [1, 2, 3, 4],'B': [[1, 2], [1, 2], [], np.nan]})
    df.B = df.B.fillna({i: [] for i in df.index})  # replace NaN with []
    df.explode('B')
    
       A    B
    0  1    1
    0  1    2
    1  2    1
    1  2    2
    2  3  NaN
    3  4  NaN
    


    apply + pd.Series (易于理解,但性能方面不推荐。)

    df.set_index('A').B.apply(pd.Series).stack().reset_index(level=0).rename(columns={0:'B'})
    Out[463]: 
       A  B
    0  1  1
    1  1  2
    0  2  1
    1  2  2
    

    方法2
    repeat 具有 DataFrame

    df=pd.DataFrame({'A':df.A.repeat(df.B.str.len()),'B':np.concatenate(df.B.values)})
    df
    Out[465]: 
       A  B
    0  1  1
    0  1  2
    1  2  1
    1  2  2
    

    方法2.1
    方法2 )在上面,我们很难一个一个地重新创建这些列。

    join merge index

    s=pd.DataFrame({'B':np.concatenate(df.B.values)},index=df.index.repeat(df.B.str.len()))
    s.join(df.drop('B',1),how='left')
    Out[477]: 
       B  A
    0  1  1
    0  2  1
    1  1  2
    1  2  2
    

    如果需要与以前完全相同的列顺序,请添加 reindex

    s.join(df.drop('B',1),how='left').reindex(columns=df.columns)
    

    方法3
    重新创建 列表

    pd.DataFrame([[x] + [z] for x, y in df.values for z in y],columns=df.columns)
    Out[488]: 
       A  B
    0  1  1
    1  1  2
    2  2  1
    3  2  2
    

    如果超过两列,则使用

    s=pd.DataFrame([[x] + [z] for x, y in zip(df.index,df.B) for z in y])
    s.merge(df,left_on=0,right_index=True)
    Out[491]: 
       0  1  A       B
    0  0  1  1  [1, 2]
    1  0  2  1  [1, 2]
    2  1  1  2  [1, 2]
    3  1  2  2  [1, 2]
    


    使用 loc

    df.reindex(df.index.repeat(df.B.str.len())).assign(B=np.concatenate(df.B.values))
    Out[554]: 
       A  B
    0  1  1
    0  1  2
    1  2  1
    1  2  2
    
    #df.loc[df.index.repeat(df.B.str.len())].assign(B=np.concatenate(df.B.values))
    

    方法5
    当列表仅包含唯一值时:

    df=pd.DataFrame({'A':[1,2],'B':[[1,2],[3,4]]})
    from collections import ChainMap
    d = dict(ChainMap(*map(dict.fromkeys, df['B'], df['A'])))
    pd.DataFrame(list(d.items()),columns=df.columns[::-1])
    Out[574]: 
       B  A
    0  1  1
    1  2  1
    2  3  2
    3  4  2
    


    使用 高性能:

    newvalues=np.dstack((np.repeat(df.A.values,list(map(len,df.B.values))),np.concatenate(df.B.values)))
    pd.DataFrame(data=newvalues[0],columns=df.columns)
       A  B
    0  1  1
    1  1  2
    2  2  1
    3  2  2
    


    使用基函数 itertools cycle chain :纯粹的python解决方案只是为了好玩

    from itertools import cycle,chain
    l=df.values.tolist()
    l1=[list(zip([x[0]], cycle(x[1])) if len([x[0]]) > len(x[1]) else list(zip(cycle([x[0]]), x[1]))) for x in l]
    pd.DataFrame(list(chain.from_iterable(l1)),columns=df.columns)
       A  B
    0  1  1
    1  1  2
    2  2  1
    3  2  2
    

    推广到多列

    df=pd.DataFrame({'A':[1,2],'B':[[1,2],[3,4]],'C':[[1,2],[3,4]]})
    df
    Out[592]: 
       A       B       C
    0  1  [1, 2]  [1, 2]
    1  2  [3, 4]  [3, 4]
    

    def unnesting(df, explode):
        idx = df.index.repeat(df[explode[0]].str.len())
        df1 = pd.concat([
            pd.DataFrame({x: np.concatenate(df[x].values)}) for x in explode], axis=1)
        df1.index = idx
    
        return df1.join(df.drop(explode, 1), how='left')
    
            
    unnesting(df,['B','C'])
    Out[609]: 
       B  C  A
    0  1  1  1
    0  2  2  1
    1  3  3  2
    1  4  4  2
    

    按列取消测试

    以上所有方法都是关于 如果你真的需要扩展列表的话,那么就取消搜索并爆炸 ,检查 pd.DataFrame 建造师

    df.join(pd.DataFrame(df.B.tolist(),index=df.index).add_prefix('B_'))
    Out[33]: 
       A       B       C  B_0  B_1
    0  1  [1, 2]  [1, 2]    1    2
    1  2  [3, 4]  [3, 4]    3    4
    

    def unnesting(df, explode, axis):
        if axis==1:
            idx = df.index.repeat(df[explode[0]].str.len())
            df1 = pd.concat([
                pd.DataFrame({x: np.concatenate(df[x].values)}) for x in explode], axis=1)
            df1.index = idx
    
            return df1.join(df.drop(explode, 1), how='left')
        else :
            df1 = pd.concat([
                             pd.DataFrame(df[x].tolist(), index=df.index).add_prefix(x) for x in explode], axis=1)
            return df1.join(df.drop(explode, 1), how='left')
    

    unnesting(df, ['B','C'], axis=0)
    Out[36]: 
       B0  B1  C0  C1  A
    0   1   2   1   2  1
    1   3   4   3   4  2
    

    用原来的explode函数更新2021-02-17

    def unnesting(df, explode, axis):
        if axis==1:
            df1 = pd.concat([df[x].explode() for x in explode], axis=1)
            return df1.join(df.drop(explode, 1), how='left')
        else :
            df1 = pd.concat([
                             pd.DataFrame(df[x].tolist(), index=df.index).add_prefix(x) for x in explode], axis=1)
            return df1.join(df.drop(explode, 1), how='left')