【问题标题】:Pandas - Interleave / Zip two DataFrames by rowPandas - 按行交错/压缩两个数据帧
【发布时间】:2020-04-01 19:46:09
【问题描述】:

假设我有两个数据框:

>> df1

   0  1  2
0  a  b  c
1  d  e  f

>> df2

   0  1  2
0  A  B  C
1  D  E  F

我怎样才能交错行?即得到这个:

>> interleaved_df

   0  1  2
0  a  b  c
1  A  B  C
2  d  e  f
3  D  E  F

(请注意,我的真实 DF 具有相同的列,但行数不同)。


我尝试过的

灵感来自this question(非常相似,但在上询问):

import pandas as pd
from itertools import chain, zip_longest

df1 = pd.DataFrame([['a','b','c'], ['d','e','f']])  
df2 = pd.DataFrame([['A','B','C'], ['D','E','F']])

concat_df = pd.concat([df1,df2])

new_index = chain.from_iterable(zip_longest(df1.index, df2.index))
# new_index now holds the interleaved row indices

interleaved_df = concat_df.reindex(new_index)

ValueError: cannot reindex from a duplicate axis

最后一次调用失败是因为 df1 和 df2 有一些相同的索引值(我的真实 DF 也是这种情况)。

有什么想法吗?

【问题讨论】:

    标签: python pandas dataframe


    【解决方案1】:

    您可以在连接后对索引进行排序,然后重新设置索引,即

    import pandas as pd
    
    df1 = pd.DataFrame([['a','b','c'], ['d','e','f']])  
    df2 = pd.DataFrame([['A','B','C'], ['D','E','F']])
    
    concat_df = pd.concat([df1,df2]).sort_index().reset_index(drop=True)
    

    输出:

    0 1 2 0 a b c 1 A B C 二维 3 D E F

    EDIT (OmerB) : 如果不管索引值如何都保持顺序。

    import pandas as pd
    df1 = pd.DataFrame([['a','b','c'], ['d','e','f']]).reset_index()  
    df2 = pd.DataFrame([['A','B','C'], ['D','E','F']]).reset_index()
    
    concat_df = pd.concat([df1,df2]).sort_index().set_index('index')
    

    【讨论】:

    • 谢谢!尝试此操作时,它不起作用,因为我的原始 DF 没有单调索引(即第二行的索引可以比第一行小。对不起,我意识到从我的玩具示例中并不明显已经发布...)。但无论如何 - 根据您的回答,我只需将 reset_index 添加到原始 DF 和 set_index 之后,它就可以工作了)。
    • 新手问题 - 我已通过我的修复提交了对您的答案的编辑。我应该等到它被接受后再接受答案吗?
    • 请注意,您应该使用.sort_index(kind="mergesort") 来确保订单的保存。合并排序是稳定的,快速排序(默认)不是。
    【解决方案2】:

    使用toolz.interleave

    In [1024]: from toolz import interleave
    
    In [1025]: pd.DataFrame(interleave([df1.values, df2.values]))
    Out[1025]:
       0  1  2
    0  a  b  c
    1  A  B  C
    2  d  e  f
    3  D  E  F
    

    【讨论】:

    • 这个非常优雅,但不适用于我的情况,因为我们丢失了原始 DataFrame 中的元数据(索引、数据类型等)
    【解决方案3】:

    这是@Bharath 答案的扩展,可以使用pd.MultiIndex 应用于具有用户定义索引的DataFrame 而不会丢失它们。

    使用完整的列/索引标签和名称定义数据框:

    df1 = pd.DataFrame([['a','b','c'], ['d','e','f']], index=['one', 'two'], columns=['col_a', 'col_b','col_c'])  
    df1.columns.name = 'cols'
    df1.index.name = 'rows'
    df2 = pd.DataFrame([['A','B','C'], ['D','E','F']], index=['one', 'two'], columns=['col_a', 'col_b','col_c'])
    df2.columns.name = 'cols'
    df2.index.name = 'rows'
    

    将 DataFrame ID 添加到 MultiIndex:

    df1.index = pd.MultiIndex.from_product([[1], df1.index], names=["df_id", df1.index.name])
    df2.index = pd.MultiIndex.from_product([[2], df2.index], names=["df_id", df2.index.name])
    

    然后使用@Bharath 的concat()sort_index()

    data = pd.concat([df1, df2], axis=0, sort=True)
    data.sort_index(axis=0, level=data.index.names[::-1], inplace=True)
    

    输出:

    cols       col_a col_b col_c
    df_id rows                  
    1     one      a     b     c
    2     one      A     B     C
    1     two      d     e     f
    2     two      D     E     F
    
    

    【讨论】:

      【解决方案4】:

      您也可以预先分配一个新的DataFrame,然后使用切片填充它。

      def interleave(dfs):
          data = np.transpose(np.array([np.empty(dfs[0].shape[0]*len(dfs), dtype=dt) for dt in dfs[0].dtypes]))
          out = pd.DataFrame(data, columns=dfs[0].columns)
          for ix, df in enumerate(dfs):
              out.iloc[ix::len(dfs),:] = df.values
          return out
      

      预分配代码取自this question

      虽然对于某些数据类型/大小,它有可能优于索引方法,但如果 DataFrame 的大小不同,它的行为就不会优雅。

      注意 - 对于约 200000 行和 20 列的混合字符串、整数和浮点类型,索引方法大约快 5 倍。

      【讨论】:

        【解决方案5】:

        你可以试试这个方法:

        In [31]: import pandas as pd
            ...: from itertools import chain, zip_longest
            ...: 
            ...: df1 = pd.DataFrame([['a','b','c'], ['d','e','f']])  
            ...: df2 = pd.DataFrame([['A','B','C'], ['D','E','F']])
        
        In [32]: concat_df = pd.concat([df1,df2]).sort_index()
            ...: 
        
        In [33]: interleaved_df = concat_df.reset_index(drop=1)
        
        In [34]: interleaved_df
        Out[34]: 
           0  1  2
        0  a  b  c
        1  A  B  C
        2  d  e  f
        3  D  E  F
        

        【讨论】:

          猜你喜欢
          • 2016-03-22
          • 1970-01-01
          • 1970-01-01
          • 2020-12-22
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2016-03-02
          • 2020-09-02
          相关资源
          最近更新 更多