【问题标题】:pandas column containing list of objects, split this column based upon keynames and store values as comma separated values包含对象列表的 pandas 列,根据键名拆分此列并将值存储为逗号分隔值
【发布时间】:2018-04-28 05:01:14
【问题描述】:

我有一个包含列的数据框:

A
[{"A": 28, "B": "abc"},{"A": 29, "B": "def"},{"A": 30, "B": "hij"}]
[{"A": 31, "B": "hij"},{"A": 32, "B": "abc"}]
[{"A": 28, "B": "abc"}]
[{"A": 28, "B": "abc"},{"A": 29, "B": "def"},{"A": 30, "B": "hij"}]
[{"A": 28, "B": "abc"},{"A": 29, "B": "klm"},{"A": 30, "B": "nop"}]
[{"A": 28, "B": "abc"},{"A": 29, "B": "xyz"}]

输出应该是这样的:

A              B
28,29,30       abc,def,hij
31,32          hij,abc
28             abc
28,29,30       abc,def,hij
28,29,30       abc,klm,nop
28,29          abc,xyz

如何根据键名将对象列表拆分为列,并将它们存储为逗号分隔值,如上所示。

【问题讨论】:

    标签: python json list pandas dataframe


    【解决方案1】:

    我想我会试一试。首先,永远不要在可以避免的地方使用eval。更好的解决方案是使用ast:

    import ast
    df.A = df.A.apply(ast.literal_eval)
    

    接下来,展平列:

    i = df.A.str.len().cumsum()   # we'll need this later
    df = pd.DataFrame.from_dict(np.concatenate(df.A).tolist())
    df.A = df.A.astype(str)
    
    df
    
         A    B
    0   28  abc
    1   29  def
    2   30  hij
    3   31  hij
    4   32  abc
    5   28  abc
    6   28  abc
    7   29  def
    8   30  hij
    9   28  abc
    10  29  klm
    11  30  nop
    12  28  abc
    13  29  xyz
    

    现在,使用i 的间隔执行groupby

    idx = pd.cut(df.index, bins=np.append([0], i), include_lowest=True, right=False)
    df = df.groupby(idx, as_index=False).agg(','.join)
    
    df
    
              A            B
    0  28,29,30  abc,def,hij
    1     31,32      hij,abc
    2        28          abc
    3  28,29,30  abc,def,hij
    4  28,29,30  abc,klm,nop
    5     28,29      abc,xyz
    

    得到了 Bharath here 的一点帮助。


    IntervalIndex (proposed by Wen) 的一个很酷的替代方案是使用 np.put

    i = df.A.str.len().cumsum()  
    df = pd.DataFrame.from_dict(np.concatenate(df.A).tolist())
    df.A = df.A.astype(str)
    
    v = pd.Series(0, index=df.index)
    np.put(v, i-1, [1] * len(i))
    
    df = df.groupby(v[::-1].cumsum()).agg(','.join)[::-1].reset_index(drop=True)
    
    df
    
              A            B
    0  28,29,30  abc,def,hij
    1     31,32      hij,abc
    2        28          abc
    3  28,29,30  abc,def,hij
    4  28,29,30  abc,klm,nop
    5     28,29      abc,xyz
    

    性能

    df = pd.concat([df] * 1000, ignore_index=True)
    
    %%timeit 
    df.A.apply(pd.Series).stack().\
         apply(pd.Series).groupby(level=0).\
            agg(lambda x :','.join(x.astype(str)))
    
    1 loop, best of 3: 8.76 s per loop
    
    %%timeit 
    A = df.A.values.tolist()
    B = {
        (i, j, k): v
        for j, row in enumerate(A)
        for i, d in enumerate(row)
        for k, v in d.items()
    }    
    pd.Series(B).astype(str).groupby(level=[1, 2]).apply(','.join).unstack()
    
    1 loop, best of 3: 2.08 s per loop
    
    %%timeit
    i = df.A.str.len().cumsum() 
    df2 = pd.DataFrame.from_dict(np.concatenate(df.A).tolist())
    df2.A = df2.A.astype(str)
    idx = pd.cut(df2.index, bins=np.append([0], i), include_lowest=True, right=False)
    df2.groupby(idx, as_index=False).agg(','.join)
    
    1 loop, best of 3: 810 ms per loop
    
    %%timeit
    i = df.A.str.len().cumsum() 
    df2 = pd.DataFrame.from_dict(np.concatenate(df.A).tolist())
    df2.A = df2.A.astype(str)
    v = pd.Series(0, index=df2.index)
    np.put(v, i-1, [1] * len(i))
    df2.groupby(v[::-1].cumsum()).agg(','.join)[::-1].reset_index(drop=True)
    
    1 loop, best of 3: 548 ms per loop
    

    【讨论】:

    • 现在我看到了。 +1
    • @Bharath 谢谢你的夸奖。我不确定pd.cut 是否是最有效的方法,但它是最先发生的。
    • 不错的一个! :-)
    • 另外,我在你的问题中添加了一个 np.put 方法,你能测试一下它的时间吗?
    • @Wen 当然,当我在我的电脑上时会这样做并让你知道。
    【解决方案2】:

    我假设A 是一个字典列表

    A = [
        [{"A": 28, "B": "abc"},{"A": 29, "B": "def"},{"A": 30, "B": "hij"}],
        [{"A": 31, "B": "hij"},{"A": 32, "B": "abc"}],
        [{"A": 28, "B": "abc"}],
        [{"A": 28, "B": "abc"},{"A": 29, "B": "def"},{"A": 30, "B": "hij"}],
        [{"A": 28, "B": "abc"},{"A": 29, "B": "klm"},{"A": 30, "B": "nop"}],
        [{"A": 28, "B": "abc"},{"A": 29, "B": "xyz"}]
    ]
    

    我要做的第一件事是使用推导式来创建一个新字典。然后','.joingroupby

    B = {
        (i, j, k): v
        for j, row in enumerate(A)
        for i, d in enumerate(row)
        for k, v in d.items()
    }
    
    pd.Series(B).astype(str).groupby(level=[1, 2]).apply(','.join).unstack()
    
              A            B
    0  28,29,30  abc,def,hij
    1     31,32      hij,abc
    2        28          abc
    3  28,29,30  abc,def,hij
    4  28,29,30  abc,klm,nop
    5     28,29      abc,xyz
    

    【讨论】:

    • 添加了一个您可能感兴趣的答案(包括时间)。
    • 恭喜 100k! :-)
    【解决方案3】:

    使用stack 然后groupby

    df.A.apply(pd.Series).stack().\
         apply(pd.Series).groupby(level=0).\
            agg(lambda x :','.join(x.astype(str)))
    Out[457]: 
              A            B
    0  28,29,30  abc,def,hij
    1     31,32      hij,abc
    2        28          abc
    3  28,29,30  abc,def,hij
    4  28,29,30  abc,klm,nop
    

    数据输入:

    df=pd.DataFrame({'A':[[{"A": 28, "B": "abc"},{"A": 29, "B": "def"},{"A": 30, "B": "hij"}],
    [{"A": 31, "B": "hij"},{"A": 32, "B": "abc"}],
    [{"A": 28, "B": "abc"}],[{"A": 28, "B": "abc"},{"A": 29, "B": "def"},{"A": 30, "B": "hij"}],
    [{"A": 28, "B": "abc"},{"A": 29, "B": "klm"},{"A": 30, "B": "nop"}]]})
    

    对于您从 csv 读取的其他问题

    import ast
    df=pd.read_csv(r'your.csv',dtype={'A':object})
    
    df['A'] = df['A'].apply(ast.literal_eval)
    

    【讨论】:

    • 我比我想的更喜欢你的(-:我会想出一些东西
    • 在应用此代码之前,原始列的类型应该是什么?
    • @NikitaGupta 字典列表
    • 我已经从 csv 导入了这些数据,如何将此列的类型转换为 dict 列表?
    • @Wen,我已经从 csv 导入了这个数据,如何将此列的类型转换为 dict 列表?
    猜你喜欢
    • 1970-01-01
    • 2018-11-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-09-30
    • 2018-07-27
    • 2015-10-06
    相关资源
    最近更新 更多