开发者社区> 问答> 正文

如何在panda DataFrame中拆出(爆炸)一个列?

我有以下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

我该怎么做才能达到这个目标? 相关的问题 当单元格内容是列表时,为列表中的每个元素创建一行 好问题和答案只处理一列列表(在我的答案self-def函数将多个列,也接受的答案是使用最耗费时间申请,不推荐,查看更多信息当我应该想要使用熊猫应用()在我的代码吗?) 问题来源StackOverflow 地址:/questions/59383423/repeat-values-in-column-for-each-value-related-in-another-column

展开
收起
kun坤 2019-12-27 10:24:46 544 0
1 条回答
写回答
取消 提交回答
  • 作为一个同时使用R和python的用户,我已经多次遇到过这种类型的问题。 在R中,它们有来自包tidyr的内置函数unnest。但是在Python(panda)中,没有针对这类问题的内置函数。 我知道对象列类型总是使数据难以转换成panda的函数。当我收到这样的数据时,首先想到的是“平铺”或取消列的嵌套。 对于这类问题,我使用了panda和python函数。如果您担心上述解决方案的速度,请检查user3483203的答案,因为他正在使用numpy,而且大多数时候numpy更快。如果速度对您很重要,我推荐Cpython和numba。 方法0[熊猫>= 0.25] 从panda 0.25开始,如果你只需要爆炸一列,你可以使用爆炸函数:

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

    方法1 应用+ pd。系列(容易理解,但在性能方面不推荐)。

    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 对DataFrame构造函数使用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 例如,除了A,我们还有A。如果我们仍然使用上面的方法(方法2),我们就很难一个一个地重新创建列。 解决方案:在“取消嵌套”单个列之后加入或合并索引

    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]
    

    方法4 使用重索引或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
    

    方法6 使用numpy实现高性能:

    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
    

    方法7 使用基本函数itertools循环和链:纯粹的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]
    

    Self-def功能:

    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
    
    2019-12-27 10:25:01
    赞同 展开评论 打赏
问答地址:
问答排行榜
最热
最新

相关电子书

更多
低代码开发师(初级)实战教程 立即下载
冬季实战营第三期:MySQL数据库进阶实战 立即下载
阿里巴巴DevOps 最佳实践手册 立即下载