pandas:如何将一列中的文本分成多行?

我正在处理一个大的csv文件,最后一列的下一列有一串文本,我想通过一个特定的分隔符来分割。 我想知道是否有一个简单的方法来做到这一点使用pandas或Python?

CustNum CustomerName ItemQty Item Seatblocks ItemExt 32363 McCartney, Paul 3 F04 2:218:10:4,6 60 31316 Lennon, John 25 F01 1:13:36:1,12 1:13:37:1,13 300 

我想分割的空间(' ') ,然后冒号(':')Seatblocks列,但每个单元格会导致不同数量的列。 我有一个函数重新排列列,所以Seatblocks列是在工作表的末尾,但我不知道该怎么做。 我可以在excel中使用内置的text-to-columns函数和一个快速macros来实现,但是我的数据集有太多的excel处理logging。

最终,我想logging约翰·列侬的logging,并创build多行,每个座位的信息在一个单独的行上。

这将Seatblocks分开空间,并给每个自己的行。

 In [43]: df Out[43]: CustNum CustomerName ItemQty Item Seatblocks ItemExt 0 32363 McCartney, Paul 3 F04 2:218:10:4,6 60 1 31316 Lennon, John 25 F01 1:13:36:1,12 1:13:37:1,13 300 In [44]: s = df['Seatblocks'].str.split(' ').apply(Series, 1).stack() In [45]: s.index = s.index.droplevel(-1) # to line up with df's index In [46]: s.name = 'Seatblocks' # needs a name to join In [47]: s Out[47]: 0 2:218:10:4,6 1 1:13:36:1,12 1 1:13:37:1,13 Name: Seatblocks, dtype: object In [48]: del df['Seatblocks'] In [49]: df.join(s) Out[49]: CustNum CustomerName ItemQty Item ItemExt Seatblocks 0 32363 McCartney, Paul 3 F04 60 2:218:10:4,6 1 31316 Lennon, John 25 F01 300 1:13:36:1,12 1 31316 Lennon, John 25 F01 300 1:13:37:1,13 

或者,给每个冒号分隔的string在自己的列:

 In [50]: df.join(s.apply(lambda x: Series(x.split(':')))) Out[50]: CustNum CustomerName ItemQty Item ItemExt 0 1 2 3 0 32363 McCartney, Paul 3 F04 60 2 218 10 4,6 1 31316 Lennon, John 25 F01 300 1 13 36 1,12 1 31316 Lennon, John 25 F01 300 1 13 37 1,13 

这有点丑陋,但也许有人会用更漂亮的解决scheme。

与丹不同的是,我认为他的回答非常优雅……但不幸的是,这样做效率也很低。 所以,既然这个问题提到了“一个大的csv文件” ,让我build议尝试壳丹的解决scheme:

 time python -c "import pandas as pd; df = pd.DataFrame(['ab c']*100000, columns=['col']); print df['col'].apply(lambda x : pd.Series(x.split(' '))).head()" 

…与这个select相比:

 time python -c "import pandas as pd; from scipy import array, concatenate; df = pd.DataFrame(['ab c']*100000, columns=['col']); print pd.DataFrame(concatenate(df['col'].apply( lambda x : [x.split(' ')]))).head()" 

… 和这个:

 time python -c "import pandas as pd; df = pd.DataFrame(['ab c']*100000, columns=['col']); print pd.DataFrame(dict(zip(range(3), [df['col'].apply(lambda x : x.split(' ')[i]) for i in range(3)]))).head()" 

第二个简单地不用分配100000个系列,这足以使其快10倍左右。 但是第三种解决scheme有点讽刺的是浪费了大量的对str.split()的调用(每行每列调用一次,比其他两种解决scheme多三倍)比第一种解决scheme快40倍左右,因为它甚至避免了100 000个列表。 是的,这当然有点难看…

编辑: 这个答案build议如何使用“to_list()”,并避免需要一个lambda。 结果是类似的

 time python -c "import pandas as pd; df = pd.DataFrame(['ab c']*100000, columns=['col']); print pd.DataFrame(df.col.str.split().tolist()).head()" 

这比第三种解决scheme更有效率,而且更加优雅。

编辑:更简单

 time python -c "import pandas as pd; df = pd.DataFrame(['ab c']*100000, columns=['col']); print pd.DataFrame(list(df.col.str.split())).head()" 

也有效,而且几乎同样有效。

编辑: 更简单 ! 并处理NaN(但效率较低):

 time python -c "import pandas as pd; df = pd.DataFrame(['ab c']*100000, columns=['col']); print df.col.str.split(expand=True).head()" 
 import pandas as pd import numpy as np df = pd.DataFrame({'ItemQty': {0: 3, 1: 25}, 'Seatblocks': {0: '2:218:10:4,6', 1: '1:13:36:1,12 1:13:37:1,13'}, 'ItemExt': {0: 60, 1: 300}, 'CustomerName': {0: 'McCartney, Paul', 1: 'Lennon, John'}, 'CustNum': {0: 32363, 1: 31316}, 'Item': {0: 'F04', 1: 'F01'}}, columns=['CustNum','CustomerName','ItemQty','Item','Seatblocks','ItemExt']) print (df) CustNum CustomerName ItemQty Item Seatblocks ItemExt 0 32363 McCartney, Paul 3 F04 2:218:10:4,6 60 1 31316 Lennon, John 25 F01 1:13:36:1,12 1:13:37:1,13 300 

链接的另一个类似的解决scheme是使用reset_indexrename

 print (df.drop('Seatblocks', axis=1) .join ( df.Seatblocks .str .split(expand=True) .stack() .reset_index(drop=True, level=1) .rename('Seatblocks') )) CustNum CustomerName ItemQty Item ItemExt Seatblocks 0 32363 McCartney, Paul 3 F04 60 2:218:10:4,6 1 31316 Lennon, John 25 F01 300 1:13:36:1,12 1 31316 Lennon, John 25 F01 300 1:13:37:1,13 

如果在列中不是 NaN值,最快的解决scheme是使用DataFrame构造函数的list理解:

 df = pd.DataFrame(['ab c']*100000, columns=['col']) In [141]: %timeit (pd.DataFrame(dict(zip(range(3), [df['col'].apply(lambda x : x.split(' ')[i]) for i in range(3)])))) 1 loop, best of 3: 211 ms per loop In [142]: %timeit (pd.DataFrame(df.col.str.split().tolist())) 10 loops, best of 3: 87.8 ms per loop In [143]: %timeit (pd.DataFrame(list(df.col.str.split()))) 10 loops, best of 3: 86.1 ms per loop In [144]: %timeit (df.col.str.split(expand=True)) 10 loops, best of 3: 156 ms per loop In [145]: %timeit (pd.DataFrame([ x.split() for x in df['col'].tolist()])) 10 loops, best of 3: 54.1 ms per loop 

但是,如果列包含NaN只能使用str.split参数expand=True返回DataFrame ( 文档 ),并解释为什么它更慢:

 df = pd.DataFrame(['ab c']*10, columns=['col']) df.loc[0] = np.nan print (df.head()) col 0 NaN 1 abc 2 abc 3 abc 4 abc print (df.col.str.split(expand=True)) 0 1 2 0 NaN None None 1 abc 2 abc 3 abc 4 abc 5 abc 6 abc 7 abc 8 abc 9 abc