如何在熊猫数据框中拆分元组列?


88

我有一个熊猫数据框(这只是一小块)

>>> d1
   y norm test  y norm train  len(y_train)  len(y_test)  \
0    64.904368    116.151232          1645          549   
1    70.852681    112.639876          1645          549   

                                    SVR RBF  \
0   (35.652207342877873, 22.95533537448393)   
1  (39.563683797747622, 27.382483096332511)   

                                        LCV  \
0  (19.365430594452338, 13.880062435173587)   
1  (19.099614489458364, 14.018867136617146)   

                                   RIDGE CV  \
0  (4.2907610988480362, 12.416745648065584)   
1    (4.18864306788194, 12.980833914392477)   

                                         RF  \
0   (9.9484841581029428, 16.46902345373697)   
1  (10.139848213735391, 16.282141345406522)   

                                           GB  \
0  (0.012816232716538605, 15.950164822266007)   
1  (0.012814519804493328, 15.305745202851712)   

                                             ET DATA  
0  (0.00034337162272515505, 16.284800366214057)  j2m  
1  (0.00024811554516431878, 15.556506191784194)  j2m  
>>> 

我想拆分所有包含元组的列。比如我想替换列LCV与列LCV-aLCV-b

我怎样才能做到这一点?

Answers:


159

您可以通过pd.DataFrame(col.tolist())在该列上执行此操作:

In [2]: df = pd.DataFrame({'a':[1,2], 'b':[(1,2), (3,4)]})                                                                                                                      

In [3]: df                                                                                                                                                                      
Out[3]: 
   a       b
0  1  (1, 2)
1  2  (3, 4)

In [4]: df['b'].tolist()                                                                                                                                                        
Out[4]: [(1, 2), (3, 4)]

In [5]: pd.DataFrame(df['b'].tolist(), index=df.index)                                                                                                                                          
Out[5]: 
   0  1
0  1  2
1  3  4

In [6]: df[['b1', 'b2']] = pd.DataFrame(df['b'].tolist(), index=df.index)                                                                                                                       

In [7]: df                                                                                                                                                                      
Out[7]: 
   a       b  b1  b2
0  1  (1, 2)   1   2
1  2  (3, 4)   3   4

注意:在早期版本中,建议使用此答案df['b'].apply(pd.Series)代替pd.DataFrame(df['b'].tolist(), index=df.index)。这也很好(因为它使每个元组组成一个Series,然后将其视为一个数据帧的一行),但是比tolist版本更慢/使用了更多的内存,如此处其他答案所述(感谢@denfromufa) 。
我更新了此答案,以确保最可见的答案具有最佳的解决方案。


2
由于列数众多,有没有办法使它自动化?
Donbeo 2015年

我认为不直接。但是您可以使用上面的代码轻松地为它编写一个函数(+删除原始代码)
joris

如果您有大量的列,则可能需要考虑“整理”您的数据:vita.had.co.nz/papers/tidy-data.html您可以使用melt函数来做到这一点。
Axel

.apply(pd.Series)可以正常工作,但是对于大型数据集,它将占用大量内存,并且可能导致内存错误
Yury Wallet

26

在更大的数据集上,我发现.apply()慢了几个订单pd.DataFrame(df['b'].values.tolist(), index=df.index)

尽管我不同意这个决定,但该性能问题已在GitHub中关闭:

https://github.com/pandas-dev/pandas/issues/11615

编辑:基于此答案:https//stackoverflow.com/a/44196843/2230844


5
pd.DataFrame(df['b'].tolist())没有.values似乎也很好。(并且,谢谢,您的解决方案更快.apply()
Swier 2016年

我担心捕获索引,因此明确使用.values。
denfromufa

1
@denfromufa提供的解决方案可以超级快速地运行df [[''b1','b2']] = pd.DataFrame(df ['b']。values.tolist(),index = df.index)并且不会导致内存错误(如与.apply(pd.Series)相比)
Yury

17

str是提供给访问pandas.Series的对象dtype == object实际上是一个迭代。

假设pandas.DataFrame df

df = pd.DataFrame(dict(col=[*zip('abcdefghij', range(10, 101, 10))]))

df

        col
0   (a, 10)
1   (b, 20)
2   (c, 30)
3   (d, 40)
4   (e, 50)
5   (f, 60)
6   (g, 70)
7   (h, 80)
8   (i, 90)
9  (j, 100)

我们可以测试它是否可迭代

from collections import Iterable

isinstance(df.col.str, Iterable)

True

然后,我们可以像执行其他可迭代项一样从中分配:

var0, var1 = 'xy'
print(var0, var1)

x y

最简单的解决方案

因此,我们可以在一行中分配两列

df['a'], df['b'] = df.col.str

df

        col  a    b
0   (a, 10)  a   10
1   (b, 20)  b   20
2   (c, 30)  c   30
3   (d, 40)  d   40
4   (e, 50)  e   50
5   (f, 60)  f   60
6   (g, 70)  g   70
7   (h, 80)  h   80
8   (i, 90)  i   90
9  (j, 100)  j  100

更快的解决方案

只是稍微复杂一点,我们可以zip用来创建类似的可迭代对象

df['c'], df['d'] = zip(*df.col)

df

        col  a    b  c    d
0   (a, 10)  a   10  a   10
1   (b, 20)  b   20  b   20
2   (c, 30)  c   30  c   30
3   (d, 40)  d   40  d   40
4   (e, 50)  e   50  e   50
5   (f, 60)  f   60  f   60
6   (g, 70)  g   70  g   70
7   (h, 80)  h   80  h   80
8   (i, 90)  i   90  i   90
9  (j, 100)  j  100  j  100

排队

含义,不要对现有变量进行更改df
这是因为assign采用关键字参数,其中关键字是新(或现有)列名称,而值将是新列的值。您可以使用字典并将其解包,**并将其用作关键字参数。因此,这是分配新列的巧妙方法,该列名为iterable中'g'的第一项,而df.col.striterable'h'中为第二项df.col.str

df.assign(**dict(zip('gh', df.col.str)))

        col  g    h
0   (a, 10)  a   10
1   (b, 20)  b   20
2   (c, 30)  c   30
3   (d, 40)  d   40
4   (e, 50)  e   50
5   (f, 60)  f   60
6   (g, 70)  g   70
7   (h, 80)  h   80
8   (i, 90)  i   90
9  (j, 100)  j  100

我的list做法

借助现代列表理解功能和变量拆包功能。
注意:也可以内联使用join

df.join(pd.DataFrame([*df.col], df.index, [*'ef']))

        col  g    h
0   (a, 10)  a   10
1   (b, 20)  b   20
2   (c, 30)  c   30
3   (d, 40)  d   40
4   (e, 50)  e   50
5   (f, 60)  f   60
6   (g, 70)  g   70
7   (h, 80)  h   80
8   (i, 90)  i   90
9  (j, 100)  j  100

变异版本为

df[['e', 'f']] = pd.DataFrame([*df.col], df.index)

天真的时间测试

短数据框

使用上面定义的一个

%timeit df.assign(**dict(zip('gh', df.col.str)))
%timeit df.assign(**dict(zip('gh', zip(*df.col))))
%timeit df.join(pd.DataFrame([*df.col], df.index, [*'gh']))

1.16 ms ± 21.5 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
635 µs ± 18.7 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
795 µs ± 42.5 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
长数据框

大10 ^ 3倍

df = pd.concat([df] * 1000, ignore_index=True)

%timeit df.assign(**dict(zip('gh', df.col.str)))
%timeit df.assign(**dict(zip('gh', zip(*df.col))))
%timeit df.join(pd.DataFrame([*df.col], df.index, [*'gh']))

11.4 ms ± 1.53 ms per loop (mean ± std. dev. of 7 runs, 100 loops each)
2.1 ms ± 41.4 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
2.33 ms ± 35.1 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

2
考虑添加TL; DR:df['a'], df['b'] = df.col.str:)
mirekphd

11

我认为更简单的方法是:

>>> import pandas as pd
>>> df = pd.DataFrame({'a':[1,2], 'b':[(1,2), (3,4)]}) 
>>> df
   a       b
0  1  (1, 2)
1  2  (3, 4)
>>> df['b_a']=df['b'].str[0]
>>> df['b_b']=df['b'].str[1]
>>> df
   a       b  b_a  b_b
0  1  (1, 2)    1    2
1  2  (3, 4)    3    4

1
此解决方案确实更加简单
ApplePie '19

@jinhuawang看来这是在对象str表示之上的hack pd.Series。您能解释一下它是如何工作的吗?
denfromufa

我认为这只是str对象的工作原理?您可以使用str访问数组对象
Jinhua Wang

如果某些行的元组具有不同数量的值怎么办?
mammykins,

我认为这应该被接受。如果真是这样,那就更像是“熊猫式”了。
Natacha

8

我知道这是从前一段时间开始的,但要注意第二个解决方案:

pd.DataFrame(df['b'].values.tolist())

是它将显式丢弃索引,并添加默认的顺序索引,而可接受的答案

apply(pd.Series)

不会,因为apply的结果将保留行索引。在最初从原始数组保留顺序的同时,大熊猫将尝试匹配两个数据框中的指标。

如果您尝试将行设置为数字索引数组,这将非常重要,而pa​​ndas会自动尝试将新数组的索引与旧数组进行匹配,并导致排序出现一些失真。

更好的混合解决方案是将原始数据帧的索引设置为新的索引,即

pd.DataFrame(df['b'].values.tolist(), index=df.index)

这将确保使用第二种方法的速度,同时确保在结果上保留顺序和索引。


我根据您的索引观察结果编辑了答案,谢谢!
denfromufa
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.