如何将一列中的文本拆分为多行

2024-12-27 08:46:00
admin
原创
132
摘要:问题描述:我正在处理一个大型 csv 文件,倒数第二列有一串文本,我想用特定分隔符将其拆分。我想知道是否有一种使用 pandas 或 python 的简单方法可以做到这一点?CustNum CustomerName ItemQty Item Seatblocks ...

问题描述:

我正在处理一个大型 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位于工作表的末尾,但我不知道接下来该怎么做。我可以使用内置text-to-columns函数和快速宏在 Excel 中完成此操作,但我的数据集有太多记录,Excel 无法处理。

最终,我想采用约翰列侬之类的唱片并创建多条线路,将每组座位的信息放在单独的线路上。


解决方案 1:

这将根据空间将座块分开,并为每个座块分配一排。

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

或者,将每个以冒号分隔的字符串放在其自己的列中:

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

这有点丑陋,但也许有人会提出更漂亮的解决方案。

解决方案 2:

与 Dan 不同,我认为他的回答非常优雅……但不幸的是,它也非常低效。因此,既然问题提到了“一个大的 csv 文件”,我建议在 shell 中尝试 Dan 的解决方案:

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

... 与此替代方案相比:

time python -c "import pandas as pd;
from scipy import array, concatenate;
df = pd.DataFrame(['a b 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(['a b 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()"

第二个解决方案只是避免分配 100 000 个系列,这足以使其速度提高 10 倍左右。但第三个解决方案有点讽刺地浪费了大量对 str.split() 的调用(它每行每列调用一次,因此比其他两个解决方案多三倍),它比第一个解决方案快40 倍左右,因为它甚至避免实例化 100 000 个列表。是的,它确实有点丑陋......

编辑: 这个答案建议如何使用“to_list()”并避免使用 lambda。结果类似于

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

这比第三种解决方案更有效,并且肯定更优雅。

编辑:更简单

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

也可以工作,并且几乎同样有效。

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

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

解决方案 3:

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

与链接类似的另一个解决方案是使用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值,最快的解决方案是使用构造函数list的理解DataFrame

df = pd.DataFrame(['a b 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(['a b c']*10, columns=['col'])
df.loc[0] = np.nan
print (df.head())
     col
0    NaN
1  a b c
2  a b c
3  a b c
4  a b c

print (df.col.str.split(expand=True))
     0     1     2
0  NaN  None  None
1    a     b     c
2    a     b     c
3    a     b     c
4    a     b     c
5    a     b     c
6    a     b     c
7    a     b     c
8    a     b     c
9    a     b     c

解决方案 4:

现在回答这个问题可能有点晚了,但我希望记录下 Pandas 的两个优点:pandas.Series.str.split()正则表达式和pandas.Series.explode()

import pandas as pd
import numpy as np

df = pd.DataFrame(
    {'CustNum': [32363, 31316],
     'CustomerName': ['McCartney, Paul', 'Lennon, John'],
     'ItemQty': [3, 25],
     'Item': ['F04', 'F01'],
     'Seatblocks': ['2:218:10:4,6', '1:13:36:1,12 1:13:37:1,13'],
     'ItemExt': [60, 360]
    }
)

print(df)
print('-'*80+'
')

df['Seatblocks'] = df['Seatblocks'].str.split('[ :]')
df = df.explode('Seatblocks').reset_index(drop=True)
cols = list(df.columns)
cols.append(cols.pop(cols.index('CustomerName')))
df = df[cols]


print(df)
print('='*80+'
')
print(df[df['CustomerName'] == 'Lennon, John'])

输出为:

   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      360
--------------------------------------------------------------------------------

    CustNum  ItemQty Item Seatblocks  ItemExt     CustomerName
0     32363        3  F04          2       60  McCartney, Paul
1     32363        3  F04        218       60  McCartney, Paul
2     32363        3  F04         10       60  McCartney, Paul
3     32363        3  F04        4,6       60  McCartney, Paul
4     31316       25  F01          1      360     Lennon, John
5     31316       25  F01         13      360     Lennon, John
6     31316       25  F01         36      360     Lennon, John
7     31316       25  F01       1,12      360     Lennon, John
8     31316       25  F01          1      360     Lennon, John
9     31316       25  F01         13      360     Lennon, John
10    31316       25  F01         37      360     Lennon, John
11    31316       25  F01       1,13      360     Lennon, John
================================================================================

    CustNum  ItemQty Item Seatblocks  ItemExt  CustomerName
4     31316       25  F01          1      360  Lennon, John
5     31316       25  F01         13      360  Lennon, John
6     31316       25  F01         36      360  Lennon, John
7     31316       25  F01       1,12      360  Lennon, John
8     31316       25  F01          1      360  Lennon, John
9     31316       25  F01         13      360  Lennon, John
10    31316       25  F01         37      360  Lennon, John
11    31316       25  F01       1,13      360  Lennon, John

解决方案 5:

这似乎比该帖子中其他地方建议的方法更简单。

在熊猫数据框中拆分行

解决方案 6:

另一种方法是这样的:

temp = df['Seatblocks'].str.split(' ')
data = data.reindex(data.index.repeat(temp.apply(len)))
data['new_Seatblocks'] = np.hstack(temp)

解决方案 7:

也可以使用groupby(),而不需要join和stack()。

使用上述示例数据:

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  


#first define a function: given a Series of string, split each element into a new series
def split_series(ser,sep):
    return pd.Series(ser.str.cat(sep=sep).split(sep=sep)) 
#test the function, 
split_series(pd.Series(['a b','c']),sep=' ')
0    a
1    b
2    c
dtype: object

df2=(df.groupby(df.columns.drop('Seatblocks').tolist()) #group by all but one column
          ['Seatblocks'] #select the column to be split
          .apply(split_series,sep=' ') # split 'Seatblocks' in each group
         .reset_index(drop=True,level=-1).reset_index()) #remove extra index created

print(df2)
   CustNum     CustomerName  ItemQty Item  ItemExt    Seatblocks
0    31316     Lennon, John       25  F01      300  1:13:36:1,12
1    31316     Lennon, John       25  F01      300  1:13:37:1,13
2    32363  McCartney, Paul        3  F04       60  2:218:10:4,6
相关推荐
  政府信创国产化的10大政策解读一、信创国产化的背景与意义信创国产化,即信息技术应用创新国产化,是当前中国信息技术领域的一个重要发展方向。其核心在于通过自主研发和创新,实现信息技术应用的自主可控,减少对外部技术的依赖,并规避潜在的技术制裁和风险。随着全球信息技术竞争的加剧,以及某些国家对中国在科技领域的打压,信创国产化显...
工程项目管理   3983  
  为什么项目管理通常仍然耗时且低效?您是否还在反复更新电子表格、淹没在便利贴中并参加每周更新会议?这确实是耗费时间和精力。借助软件工具的帮助,您可以一目了然地全面了解您的项目。如今,国内外有足够多优秀的项目管理软件可以帮助您掌控每个项目。什么是项目管理软件?项目管理软件是广泛行业用于项目规划、资源分配和调度的软件。它使项...
项目管理软件   2747  
  本文介绍了以下10款项目管理软件工具:禅道项目管理软件、Freshdesk、ClickUp、nTask、Hubstaff、Plutio、Productive、Targa、Bonsai、Wrike。在当今快速变化的商业环境中,项目管理已成为企业成功的关键因素之一。然而,许多企业在项目管理过程中面临着诸多痛点,如任务分配不...
项目管理系统   82  
  本文介绍了以下10款项目管理软件工具:禅道项目管理软件、Monday、TeamGantt、Filestage、Chanty、Visor、Smartsheet、Productive、Quire、Planview。在当今快速变化的商业环境中,项目管理已成为企业成功的关键因素之一。然而,许多项目经理和团队在管理复杂项目时,常...
开源项目管理工具   90  
  本文介绍了以下10款项目管理软件工具:禅道项目管理软件、Smartsheet、GanttPRO、Backlog、Visor、ResourceGuru、Productive、Xebrio、Hive、Quire。在当今快节奏的商业环境中,项目管理已成为企业成功的关键因素之一。然而,许多企业在选择项目管理工具时常常面临困惑:...
项目管理系统   79  
热门文章
项目管理软件有哪些?
曾咪二维码

扫码咨询,免费领取项目管理大礼包!

云禅道AD
禅道项目管理软件

云端的项目管理软件

尊享禅道项目软件收费版功能

无需维护,随时随地协同办公

内置subversion和git源码管理

每天备份,随时转为私有部署

免费试用