【问题标题】:Split lists in a dataframe with different length lists in columns and rows拆分数据框中的列表,在列和行中具有不同长度的列表
【发布时间】:2021-11-14 13:55:19
【问题描述】:

所以我的问题类似于one。我的不同,因为我在同一行和同一列上有不同长度的列表。

我尝试过的许多解决方案都会产生一个非常长的数据帧,其中包含多次重复。我的要求是逐行的,这意味着如果一行有一个列表,它将被分成所需的行数,但不会导致多次重复。请看下面的例子。

输入示例

import pandas as pd
df = pd.DataFrame(
{'C1': [["A","B"], ["C"], ["D","E"], ["F"]],
 'C2': [[1], [2], [3], [4]],
 'C3': ['s1', 's2', 's3', 's4'],
 'C4': [123, 321, [777,111], 145]})

df

所需的输出示例

我一直在玩explode()reset_index()drop() 等等,但还没有得到任何东西来提供正确的输出。

我尝试过的一件事是这个

df = df.explode("C1").reset_index().drop("index",1).explode("C4").reset_index().drop("index",1)

但是输出错误

【问题讨论】:

    标签: python pandas explode


    【解决方案1】:

    似乎需要将已爆炸的列和未爆炸的列分开。由于我们不能像往常一样将它们隐藏在索引中(给定C2)包含列表(不可散列),我们必须分离 DataFrame 然后重新加入。

    # Convert to single series to explode
    cols = ['C1', 'C4']
    new_df = df[cols].stack().explode().to_frame()
    # Enumerate groups then unstack
    new_df = new_df.set_index(
        new_df.groupby(level=[0, 1]).cumcount(),
        append=True
    ).unstack(1).groupby(level=0).ffill()
    
    # Join Back Unaffected columns
    new_df = new_df.droplevel(0, axis=1).droplevel(1, axis=0).join(
        df[df.columns.symmetric_difference(cols)]
    )
    # Re order columns and reset index
    new_df = new_df.reindex(df.columns, axis=1).reset_index(drop=True)
    

    new_df:

      C1   C2  C3   C4
    0  A  [1]  s1  123
    1  B  [1]  s1  123
    2  C  [2]  s2  321
    3  D  [3]  s3  777
    4  E  [3]  s3  111
    5  F  [4]  s4  145
    

    我们stack 将所有值放入一个系列中,然后将explode 一起转换回to_frame

    cols = ['C1', 'C4']
    new_df = df[cols].stack().explode().to_frame()
    

    new_df

            0
    0 C1    A
      C1    B
      C4  123
    1 C1    C
      C4  321
    2 C1    D
      C1    E
      C4  777
      C4  111
    3 C1    F
      C4  145
    

    我们可以用groupby cumcountset_indexunstacking枚举组来创建一个新索引:

    new_df = new_df.set_index(
        new_df.groupby(level=[0, 1]).cumcount(),
        append=True
    ).unstack(1)
    
         0     
        C1   C4
    0 0  A  123
      1  B  NaN
    1 0  C  321
    2 0  D  777
      1  E  111
    3 0  F  145
    

    然后我们可以在索引组中groupby ffill

    new_df = new_df.groupby(level=0).ffill()
    

    new_df:

         0     
        C1   C4
    0 0  A  123
      1  B  123
    1 0  C  321
    2 0  D  777
      1  E  111
    3 0  F  145
    

    然后我们可以join 将未受影响的列返回到 DataFrame,reindex 以最初出现的方式重新排序它们,droplevel 以删除不需要的索引级别,最后是reset_index

    # Join Back Unaffected columns
    new_df = new_df.droplevel(0, axis=1).droplevel(1, axis=0).join(
        df[df.columns.symmetric_difference(cols)]
    )
    # Re order columns and reset index
    new_df = new_df.reindex(df.columns, axis=1).reset_index(drop=True)
    

    new_df:

      C1   C2  C3   C4
    0  A  [1]  s1  123
    1  B  [1]  s1  123
    2  C  [2]  s2  321
    3  D  [3]  s3  777
    4  E  [3]  s3  111
    5  F  [4]  s4  145
    

    【讨论】:

    • 有点麻烦。但是就像你在我的 1.3.0 更新帖子中提到的那样,这在 pandas 中不是一个很好的支持操作。
    【解决方案2】:
     df=df.explode('C4').assign(C1=df['C1'].str.join(',').str.split(',')).explode('C1')#Explode to expand dataframe
    m=df.duplicated(subset='C1', keep=False)#loc select the duplicated
    
    df.loc[m,'C4']=df.loc[m,'C4'].shift(1)#Introduce Nan
    
    df.dropna().drop_duplicates(subset='C1', keep='last')#clean dataframe
    

    输出

       C1   C2  C3   C4
    0  A  [1]  s1  123
    0  B  [1]  s1  123
    1  C  [2]  s2  321
    2  D  [3]  s3  777
    2  E  [3]  s3  111
    3  F  [4]  s4  145
    

    【讨论】:

    • OP 不希望列 C1 中出现重复值
    • 看起来这个也可以。谢谢
    猜你喜欢
    • 1970-01-01
    • 2019-12-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-02-26
    • 2020-08-25
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多