【问题标题】:How to unnest (explode) a column in a pandas DataFrame, into multiple rows如何将 pandas DataFrame 中的列取消嵌套(分解)成多行
【发布时间】:2019-04-12 14:55:56
【问题描述】:

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

【问题讨论】:

标签: python pandas dataframe


【解决方案1】:

我知道object dtype 列使数据难以使用 pandas 函数进行转换。当我收到这样的数据时,首先想到的是“展平”或取消嵌套列。

我正在使用 pandas 和 Python 函数来解决这类问题。如果您担心上述解决方案的速度,请查看user3483203's answer,因为它使用的是 numpy 并且大多数时候 numpy 更快。如果速度很重要,我推荐Cythonnumba


方法 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

给定一个数据框,其列中的 listNaN 为空。空列表不会导致问题,但NaN 需要用list 填充

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

方法二 使用 repeatDataFrame 构造函数,重新创建你的数据框(性能好,不擅长多列)

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.1 .....A.n。如果我们仍然使用上面的方法(方法2),我们很难一一重新创建列。

解决方案:joinmerge 在“取消嵌套”单个列之后使用 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)

方法三 重新创建list

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]

方法四 使用reindexloc

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))

方法五 当列表只包含唯一值时:

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 cyclechain:纯 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

按列取消嵌套

以上方法都是关于vertical unnesting and explode ,如果你确实需要展开列表horizo​​ntal,检查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

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')

【讨论】:

  • 从 1.3.0 版本开始,我们可以一次explode() 多列。
【解决方案2】:

选项 1

如果另一列中的所有子列表长度相同,numpy 在这里可能是一个有效的选择:

vals = np.array(df.B.values.tolist())    
a = np.repeat(df.A, vals.shape[1])

pd.DataFrame(np.column_stack((a, vals.ravel())), columns=df.columns)

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

选项 2

如果子列表的长度不同,则需要额外的步骤:

vals = df.B.values.tolist()
rs = [len(r) for r in vals]    
a = np.repeat(df.A, rs)

pd.DataFrame(np.column_stack((a, np.concatenate(vals))), columns=df.columns)

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

选项 3

我尝试将其概括为扁平化N 列和平铺M 列,稍后我将努力提高效率:

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

   A          B          C  D
0  1     [1, 2]  [1, 2, 3]  A
1  2  [1, 2, 3]     [1, 2]  B
2  3        [1]     [1, 2]  C

def unnest(df, tile, explode):
    vals = df[explode].sum(1)
    rs = [len(r) for r in vals]
    a = np.repeat(df[tile].values, rs, axis=0)
    b = np.concatenate(vals.values)
    d = np.column_stack((a, b))
    return pd.DataFrame(d, columns = tile +  ['_'.join(explode)])

unnest(df, ['A', 'D'], ['B', 'C'])

    A  D B_C
0   1  A   1
1   1  A   2
2   1  A   1
3   1  A   2
4   1  A   3
5   2  B   1
6   2  B   2
7   2  B   3
8   2  B   1
9   2  B   2
10  3  C   1
11  3  C   1
12  3  C   2

函数

def wen1(df):
    return df.set_index('A').B.apply(pd.Series).stack().reset_index(level=0).rename(columns={0: 'B'})

def wen2(df):
    return pd.DataFrame({'A':df.A.repeat(df.B.str.len()),'B':np.concatenate(df.B.values)})

def wen3(df):
    s = pd.DataFrame({'B': np.concatenate(df.B.values)}, index=df.index.repeat(df.B.str.len()))
    return s.join(df.drop('B', 1), how='left')

def wen4(df):
    return pd.DataFrame([[x] + [z] for x, y in df.values for z in y],columns=df.columns)

def chris1(df):
    vals = np.array(df.B.values.tolist())
    a = np.repeat(df.A, vals.shape[1])
    return pd.DataFrame(np.column_stack((a, vals.ravel())), columns=df.columns)

def chris2(df):
    vals = df.B.values.tolist()
    rs = [len(r) for r in vals]
    a = np.repeat(df.A.values, rs)
    return pd.DataFrame(np.column_stack((a, np.concatenate(vals))), columns=df.columns)

时间

import pandas as pd
import matplotlib.pyplot as plt
import numpy as np
from timeit import timeit

res = pd.DataFrame(
       index=['wen1', 'wen2', 'wen3', 'wen4', 'chris1', 'chris2'],
       columns=[10, 50, 100, 500, 1000, 5000, 10000],
       dtype=float
)

for f in res.index:
    for c in res.columns:
        df = pd.DataFrame({'A': [1, 2], 'B': [[1, 2], [1, 2]]})
        df = pd.concat([df]*c)
        stmt = '{}(df)'.format(f)
        setp = 'from __main__ import df, {}'.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)")

性能

【讨论】:

  • 有趣,很高兴知道与新的df.explode 方法的比较。
【解决方案3】:

不推荐的东西(至少在这种情况下有效):

df=pd.concat([df]*2).sort_index()
it=iter(df['B'].tolist()[0]+df['B'].tolist()[0])
df['B']=df['B'].apply(lambda x:next(it))

concat + sort_index + iter + apply + next

现在:

print(df)

是:

   A  B
0  1  1
0  1  2
1  2  1
1  2  2

如果关心索引:

df=df.reset_index(drop=True)

现在:

print(df)

是:

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

【讨论】:

    【解决方案4】:

    另一种方法是将meshgrid recipe 应用于列的行以取消嵌套:

    import numpy as np
    import pandas as pd
    
    
    def unnest(frame, explode):
        def mesh(values):
            return np.array(np.meshgrid(*values)).T.reshape(-1, len(values))
    
        data = np.vstack(mesh(row) for row in frame[explode].values)
        return pd.DataFrame(data=data, columns=explode)
    
    
    df = pd.DataFrame({'A': [1, 2], 'B': [[1, 2], [1, 2]]})
    print(unnest(df, ['A', 'B']))  # base
    print()
    
    df = pd.DataFrame({'A': [1, 2], 'B': [[1, 2], [3, 4]], 'C': [[1, 2], [3, 4]]})
    print(unnest(df, ['A', 'B', 'C']))  # multiple columns
    print()
    
    df = pd.DataFrame({'A': [1, 2, 3], 'B': [[1, 2], [1, 2, 3], [1]],
                       'C': [[1, 2, 3], [1, 2], [1, 2]], 'D': ['A', 'B', 'C']})
    
    print(unnest(df, ['A', 'B']))  # uneven length lists
    print()
    print(unnest(df, ['D', 'B']))  # different types
    print()
    

    输出

       A  B
    0  1  1
    1  1  2
    2  2  1
    3  2  2
    
       A  B  C
    0  1  1  1
    1  1  2  1
    2  1  1  2
    3  1  2  2
    4  2  3  3
    5  2  4  3
    6  2  3  4
    7  2  4  4
    
       A  B
    0  1  1
    1  1  2
    2  2  1
    3  2  2
    4  2  3
    5  3  1
    
       D  B
    0  A  1
    1  A  2
    2  B  1
    3  B  2
    4  B  3
    5  C  1
    

    【讨论】:

      【解决方案5】:

      我的 5 美分:

      df[['B', 'B2']] = pd.DataFrame(df['B'].values.tolist())
      
      df[['A', 'B']].append(df[['A', 'B2']].rename(columns={'B2': 'B'}),
                            ignore_index=True)
      

      还有 5 个

      df[['B1', 'B2']] = pd.DataFrame([*df['B']]) # if values.tolist() is too boring
      
      (pd.wide_to_long(df.drop('B', 1), 'B', 'A', '')
       .reset_index(level=1, drop=True)
       .reset_index())
      

      两者的结果相同

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

      【讨论】:

        【解决方案6】:

        因为通常子列表长度不同,并且连接/合并的计算成本要高得多。我重新测试了不同长度子列表和更正常列的方法。

        MultiIndex 应该也是一种更简单的编写方式,并且与 numpy 方式的性能几乎相同。

        令人惊讶的是,在我的实现理解方式中表现最好。

        def stack(df):
            return df.set_index(['A', 'C']).B.apply(pd.Series).stack()
        
        
        def comprehension(df):
            return pd.DataFrame([x + [z] for x, y in zip(df[['A', 'C']].values.tolist(), df.B) for z in y])
        
        
        def multiindex(df):
            return pd.DataFrame(np.concatenate(df.B.values), index=df.set_index(['A', 'C']).index.repeat(df.B.str.len()))
        
        
        def array(df):
            return pd.DataFrame(
                np.column_stack((
                    np.repeat(df[['A', 'C']].values, df.B.str.len(), axis=0),
                    np.concatenate(df.B.values)
                ))
            )
        
        
        import pandas as pd
        import matplotlib.pyplot as plt
        import numpy as np
        from timeit import timeit
        
        res = pd.DataFrame(
            index=[
                'stack',
                'comprehension',
                'multiindex',
                'array',
            ],
            columns=[1000, 2000, 5000, 10000, 20000, 50000],
            dtype=float
        )
        
        for f in res.index:
            for c in res.columns:
                df = pd.DataFrame({'A': list('abc'), 'C': list('def'), 'B': [['g', 'h', 'i'], ['j', 'k'], ['l']]})
                df = pd.concat([df] * c)
                stmt = '{}(df)'.format(f)
                setp = 'from __main__ import df, {}'.format(f)
                res.at[f, c] = timeit(stmt, setp, number=20)
        
        ax = res.div(res.min()).T.plot(loglog=True)
        ax.set_xlabel("N")
        ax.set_ylabel("time (relative)")
        

        性能

        Relative time of each method

        【讨论】:

          【解决方案7】:
          df=pd.DataFrame({'A':[1,2],'B':[[1,2],[1,2]]})
          
          pd.concat([df['A'], pd.DataFrame(df['B'].values.tolist())], axis = 1)\
            .melt(id_vars = 'A', value_name = 'B')\
            .dropna()\
            .drop('variable', axis = 1)
          
              A   B
          0   1   1
          1   2   1
          2   1   2
          3   2   2
          

          对我想到的这种方法有什么意见吗?还是同时做 concat 和 melt 被认为太“昂贵”?

          【讨论】:

            【解决方案8】:
            df=pd.DataFrame({'A':[1,2],'B':[[1,2],[1,2]]})
            
            out = pd.concat([df.loc[:,'A'],(df.B.apply(pd.Series))], axis=1, sort=False)
            
            out = out.set_index('A').stack().droplevel(level=1).reset_index().rename(columns={0:"B"})
            
                   A    B
               0    1   1
               1    1   2
               2    2   1
               3    2   2
            
            • 如果您不想创建中间对象,您可以将其实现为一个衬垫

            【讨论】:

              【解决方案9】:
              # Here's the answer to the related question in:
              # https://stackoverflow.com/q/56708671/11426125
              
              # initial dataframe
              df12=pd.DataFrame({'Date':['2007-12-03','2008-09-07'],'names':
              [['Peter','Alex'],['Donald','Stan']]})
              
              # convert dataframe to array for indexing list values (names)
              a = np.array(df12.values)  
              
              # create a new, dataframe with dimensions for unnested
              b = np.ndarray(shape = (4,2))
              df2 = pd.DataFrame(b, columns = ["Date", "names"], dtype = str)
              
              # implement loops to assign date/name values as required
              i = range(len(a[0]))
              j = range(len(a[0]))
              for x in i:
                  for y in j:
                      df2.iat[2*x+y, 0] = a[x][0]
                      df2.iat[2*x+y, 1] = a[x][1][y]
              
              # set Date column as Index
              df2.Date=pd.to_datetime(df2.Date)
              df2.index=df2.Date
              df2.drop('Date',axis=1,inplace =True)
              

              【讨论】:

                【解决方案10】:

                我将问题概括为适用于更多列。

                我的解决方案的作用总结:

                In[74]: df
                Out[74]: 
                    A   B             C             columnD
                0  A1  B1  [C1.1, C1.2]                D1
                1  A2  B2  [C2.1, C2.2]  [D2.1, D2.2, D2.3]
                2  A3  B3            C3        [D3.1, D3.2]
                
                In[75]: dfListExplode(df,['C','columnD'])
                Out[75]: 
                    A   B     C columnD
                0  A1  B1  C1.1    D1
                1  A1  B1  C1.2    D1
                2  A2  B2  C2.1    D2.1
                3  A2  B2  C2.1    D2.2
                4  A2  B2  C2.1    D2.3
                5  A2  B2  C2.2    D2.1
                6  A2  B2  C2.2    D2.2
                7  A2  B2  C2.2    D2.3
                8  A3  B3    C3    D3.1
                9  A3  B3    C3    D3.2
                

                完整示例:

                实际的爆炸分 3 行执行。 剩下的就是装饰(多列爆炸,处理字符串而不是爆炸列中的列表,...)。

                import pandas as pd
                import numpy as np
                
                df=pd.DataFrame( {'A': ['A1','A2','A3'],
                                  'B': ['B1','B2','B3'],
                                  'C': [ ['C1.1','C1.2'],['C2.1','C2.2'],'C3'],
                                  'columnD': [ 'D1',['D2.1','D2.2', 'D2.3'],['D3.1','D3.2']],
                                  })
                print('df',df, sep='\n')
                
                def dfListExplode(df, explodeKeys):
                    if not isinstance(explodeKeys, list):
                        explodeKeys=[explodeKeys]
                    # recursive handling of explodeKeys
                    if len(explodeKeys)==0:
                        return df
                    elif len(explodeKeys)==1:
                        explodeKey=explodeKeys[0]
                    else:
                        return dfListExplode( dfListExplode(df, explodeKeys[:1]), explodeKeys[1:])
                    # perform explosion/unnesting for key: explodeKey
                    dfPrep=df[explodeKey].apply(lambda x: x if isinstance(x,list) else [x]) #casts all elements to a list
                    dfIndExpl=pd.DataFrame([[x] + [z] for x, y in zip(dfPrep.index,dfPrep.values) for z in y ], columns=['explodedIndex',explodeKey])
                    dfMerged=dfIndExpl.merge(df.drop(explodeKey, axis=1), left_on='explodedIndex', right_index=True)
                    dfReind=dfMerged.reindex(columns=list(df))
                    return dfReind
                
                dfExpl=dfListExplode(df,['C','columnD'])
                print('dfExpl',dfExpl, sep='\n')
                

                感谢WeNYoBen's answer

                【讨论】:

                  【解决方案11】:

                  通过添加explode() 方法来扩展类似列表的列是simplified significantly in pandas 0.25

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

                  输出:

                     A  B
                  0  1  1
                  0  1  2
                  1  2  1
                  1  2  2
                  

                  【讨论】:

                    【解决方案12】:

                    在我的情况下,有多个列要爆炸,并且需要取消嵌套的数组的长度可变。

                    我最终应用了新的 pandas 0.25 explode 函数两次,然后删除了生成的重复项,它就完成了!

                    df = df.explode('A')
                    df = df.explode('B')
                    df = df.drop_duplicates()
                    

                    【讨论】:

                      【解决方案13】:

                      问题设置

                      假设其中有多个具有不同长度对象的列

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

                      当长度相同时,我们很容易假设不同的元素重合,应该“压缩”在一起。

                         A       B          C
                      0  1  [1, 2]     [1, 2]  # Typical to assume these should be zipped [(1, 1), (2, 2)]
                      1  2  [3, 4]  [3, 4, 5]
                      

                      但是,当我们看到不同长度的物体时,假设会受到挑战,我们是否应该“压缩”,如果是,我们如何处理其中一个物体中的多余部分。 ,也许我们想要所有对象的乘积。这会很快变大,但可能是我们想要的。

                         A       B          C
                      0  1  [1, 2]     [1, 2]
                      1  2  [3, 4]  [3, 4, 5]  # is this [(3, 3), (4, 4), (None, 5)]?
                      

                         A       B          C
                      0  1  [1, 2]     [1, 2]
                      1  2  [3, 4]  [3, 4, 5]  # is this [(3, 3), (3, 4), (3, 5), (4, 3), (4, 4), (4, 5)]
                      

                      函数

                      此函数根据参数优雅地处理zipproduct,并根据zip_longest的最长对象的长度假定zip

                      from itertools import zip_longest, product
                      
                      def xplode(df, explode, zipped=True):
                          method = zip_longest if zipped else product
                      
                          rest = {*df} - {*explode}
                      
                          zipped = zip(zip(*map(df.get, rest)), zip(*map(df.get, explode)))
                          tups = [tup + exploded
                           for tup, pre in zipped
                           for exploded in method(*pre)]
                      
                          return pd.DataFrame(tups, columns=[*rest, *explode])[[*df]]
                      

                      压缩

                      xplode(df, ['B', 'C'])
                      
                         A    B  C
                      0  1  1.0  1
                      1  1  2.0  2
                      2  2  3.0  3
                      3  2  4.0  4
                      4  2  NaN  5
                      

                      产品

                      xplode(df, ['B', 'C'], zipped=False)
                      
                         A  B  C
                      0  1  1  1
                      1  1  1  2
                      2  1  2  1
                      3  1  2  2
                      4  2  3  3
                      5  2  3  4
                      6  2  3  5
                      7  2  4  3
                      8  2  4  4
                      9  2  4  5
                      

                      新设置

                      稍微改变一下例子

                      df = pd.DataFrame({
                          'A': [1, 2],
                          'B': [[1, 2], [3, 4]],
                          'C': 'C',
                          'D': [[1, 2], [3, 4, 5]],
                          'E': [('X', 'Y', 'Z'), ('W',)]
                      })
                      
                      df
                      
                         A       B  C          D          E
                      0  1  [1, 2]  C     [1, 2]  (X, Y, Z)
                      1  2  [3, 4]  C  [3, 4, 5]       (W,)
                      

                      压缩

                      xplode(df, ['B', 'D', 'E'])
                      
                         A    B  C    D     E
                      0  1  1.0  C  1.0     X
                      1  1  2.0  C  2.0     Y
                      2  1  NaN  C  NaN     Z
                      3  2  3.0  C  3.0     W
                      4  2  4.0  C  4.0  None
                      5  2  NaN  C  5.0  None
                      

                      产品

                      xplode(df, ['B', 'D', 'E'], zipped=False)
                      
                          A  B  C  D  E
                      0   1  1  C  1  X
                      1   1  1  C  1  Y
                      2   1  1  C  1  Z
                      3   1  1  C  2  X
                      4   1  1  C  2  Y
                      5   1  1  C  2  Z
                      6   1  2  C  1  X
                      7   1  2  C  1  Y
                      8   1  2  C  1  Z
                      9   1  2  C  2  X
                      10  1  2  C  2  Y
                      11  1  2  C  2  Z
                      12  2  3  C  3  W
                      13  2  3  C  4  W
                      14  2  3  C  5  W
                      15  2  4  C  3  W
                      16  2  4  C  4  W
                      17  2  4  C  5  W
                      

                      【讨论】:

                        【解决方案14】:

                        当您有多个列要爆炸时,我还有另一种解决此问题的好方法。

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

                        我想分解列 B 和 C。首先我分解 B,然后分解 C。然后我从原始 df 中删除 B 和 C。之后,我将对 3 个 dfs 进行索引连接。

                        explode_b = df.explode('B')['B']
                        explode_c = df.explode('C')['C']
                        df = df.drop(['B', 'C'], axis=1)
                        df = df.join([explode_b, explode_c])
                        

                        【讨论】:

                          【解决方案15】:

                          下面是一个简单的水平爆炸函数,基于@BEN_YO的回答。

                          import typing
                          import pandas as pd
                          
                          def horizontal_explode(df: pd.DataFrame, col_name: str, new_columns: typing.Union[list, None]=None) -> pd.DataFrame:
                              t = pd.DataFrame(df[col_name].tolist(), columns=new_columns, index=df.index)
                              return pd.concat([df, t], axis=1)
                          
                          

                          运行示例:

                          items = [
                              ["1", ["a", "b", "c"]],
                              ["2", ["d", "e", "f"]]
                          ]
                          
                          df = pd.DataFrame(items, columns = ["col1", "col2"])
                          print(df)
                          
                          t = horizontal_explode(df=df, col_name="col2")
                          del t["col2"]
                          print(t)
                          
                          t = horizontal_explode(df=df, col_name="col2", new_columns=["new_col1", "new_col2", "new_col3"])
                          del t["col2"]
                          print(t)
                          

                          这是相关的输出:

                            col1       col2
                          0    1  [a, b, c]
                          1    2  [d, e, f]
                          
                            col1  0  1  2
                          0    1  a  b  c
                          1    2  d  e  f
                          
                            col1 new_col1 new_col2 new_col3
                          0    1        a        b        c
                          1    2        d        e        f
                          

                          【讨论】:

                            【解决方案16】:
                             demo = {'set1':{'t1':[1,2,3],'t2':[4,5,6],'t3':[7,8,9]}, 'set2':{'t1':[1,2,3],'t2':[4,5,6],'t3':[7,8,9]}, 'set3': {'t1':[1,2,3],'t2':[4,5,6],'t3':[7,8,9]}}
                             df = pd.DataFrame.from_dict(demo, orient='index') 
                            
                             print(df.head())
                             my_list=[]
                             df2=pd.DataFrame(columns=['set','t1','t2','t3'])
                            
                             for key,item in df.iterrows():
                                t1=item.t1
                                t2=item.t2
                                t3=item.t3
                                mat1=np.matrix([t1,t2,t3])
                                row1=[key,mat1[0,0],mat1[0,1],mat1[0,2]]
                                df2.loc[len(df2)]=row1
                                row2=[key,mat1[1,0],mat1[1,1],mat1[1,2]]
                                df2.loc[len(df2)]=row2
                                row3=[key,mat1[2,0],mat1[2,1],mat1[2,2]]
                                df2.loc[len(df2)]=row3
                            
                            print(df2) 
                            
                            set t1 t2 t3
                            0  set1  1  2  3
                            1  set1  4  5  6
                            2  set1  7  8  9
                            3  set2  1  2  3
                            4  set2  4  5  6
                            5  set2  7  8  9
                            6  set3  1  2  3
                            7  set3  4  5  6
                            8  set3  7  8  9   
                            

                            【讨论】:

                              猜你喜欢
                              相关资源
                              最近更新 更多