【问题标题】:Converting tuples in a row to a new columns in Dataframe, must use pandas 0.21将一行中的元组转换为 Dataframe 中的新列,必须使用 pandas 0.21
【发布时间】:2020-06-11 10:18:30
【问题描述】:

我有一个包含元组列表的列,并且想将此元组转换为新列。 (注意:必须使用 pandas 0.21;由于我的项目要求,无法升级。)请参见下面的示例:

df = pd.DataFrame(dict(a=[1,2,3],
                  b=['a', 'a', 'b'],
                  c=[[('pear', 1), ('apple', 2)], [('pear', 7), ('orange', 1)], [('apple', 9)] ]))
df

    a   b   c
0   1   a   [(pear, 1), (apple, 2)]
1   2   a   [(pear, 7), (orange, 1)]
2   3   b   [(apple, 9)]

并希望将其转换为

    a   b   fruit   value
0   1   a   pear    1
1   1   a   apple   2
2   2   a   pear    7
3   2   a   orange  1
4   3   b   apple   9

我可以做到,但效率不高,就我而言,我有超过 500K 的行。有没有更有效的方法?

更新:

下面提出的所有三个解决方案都非常适合pandas >=0.25。对于早期版本,df.explode 不是一个选项。对于pandas < 0.24,没有df.to_numpy,所以早期版本的唯一解决方案是@jezreal 的解决方案

一个小基准低于(pandas == 0.25)(令人惊讶的是explode更慢):

from itertools import product, chain

def sol_1(df):
    phase1 = (product([a],b,c) for a,b,c in df.to_numpy())
    phase2 = [(a,b,*c) for a, b, c in chain.from_iterable(phase1)]
    return pd.DataFrame(phase2, columns = ["a","b","fruit","value"])


def sol_2(df): 
    df1 = pd.DataFrame([(k, *x) for k, v in df.c.items() for x in v],
                   columns=['i','fruit','value'])
    df = df.merge(df1, left_index=True, right_on='i').drop('i', axis=1)
    return df

def sol_3(df):
    df = df.explode('c')
    df[['fruit', 'value']] = pd.DataFrame(df['c'].tolist(), index=df.index)
    del df['c']
    return df

%timeit sol_1(df)
%timeit sol_2(df)
%timeit sol_3(df)

586 µs ± 6.39 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
2.8 ms ± 206 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
3.14 ms ± 28.2 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

【问题讨论】:

    标签: python pandas tuples reshape


    【解决方案1】:

    想法是将列表理解中的值重塑为新的 DataFrame,然后使用DataFrame.merge

    df1 = pd.DataFrame([(k, *x) for k, v in df.pop('c').items() for x in v],
                       columns=['i','fruit','value'])
    
    print (df1)
       i   fruit  value
    0  0    pear      1
    1  0   apple      2
    2  1    pear      7
    3  1  orange      1
    4  2   apple      9        
    
    df = df.merge(df1, left_index=True, right_on='i').drop('i', axis=1)
    print (df)
       a  b   fruit  value
    0  1  a    pear      1
    1  1  a   apple      2
    2  2  a    pear      7
    3  2  a  orange      1
    4  3  b   apple      9
    

    【讨论】:

    • 大熊猫唯一的解决方案,仍然比爆炸更快。谢谢,赞!
    【解决方案2】:

    试一试,看看它是否适用于您的版本:

    from itertools import product,chain
    
    #create a cartesian for each row in df
    phase1 = (product([a],b,c) for a,b,c in df.to_numpy())
    
    #unpack the third entry per row in the flattened iterable
    phase2 = [(a,b,*c) for a, b, c in chain.from_iterable(phase1)]
    
    #create dataframe
    result = pd.DataFrame(phase2, columns = ["a","b","fruit","value"])
    
    
        a   b   fruit   value
    0   1   a   pear    1
    1   1   a   apple   2
    2   2   a   pear    7
    3   2   a   orange  1
    4   3   b   apple   9
    

    【讨论】:

    • itertools 的使用非常好,谢谢,它比 df.explode 解决方案更快,甚至更好。谢谢@sammywemmy
    【解决方案3】:

    也许你可以这样尝试:

    df = pd.DataFrame(dict(a=[1,2,3],
                      b=['a', 'a', 'b'],
                      c=[[('pear', 1), ('apple', 2)], [('pear', 7), ('orange', 1)], [('apple', 9)] ]))
    
    df = df.explode('c')
    
    df[['fruit', 'value']] = pd.DataFrame(df['c'].tolist(), index=df.index)
    del df['c']
    
    

    【讨论】:

    • 不能使用,因为New in version 0.25.0.explode,OP需要pandas 0.21中的解决方案
    • 哦.. 我忽略了她对 0.21 的限制。
    猜你喜欢
    • 2018-05-29
    • 2021-11-08
    • 1970-01-01
    • 2018-12-27
    • 2019-02-15
    • 2017-11-08
    • 2014-11-26
    • 2019-06-20
    相关资源
    最近更新 更多