如何将数据框字符串列拆分为两列?
- 2024-11-26 08:37:00
- admin 原创
- 174
问题描述:
我有一个带有一列(字符串)的数据框,我想将其拆分为两列(字符串),一列标题为 ' fips'
,另一列标题为'row'
我的数据框df
如下所示:
row
0 00000 UNITED STATES
1 01000 ALABAMA
2 01001 Autauga County, AL
3 01003 Baldwin County, AL
4 01005 Barbour County, AL
我不知道如何使用df.row.str[:]
来实现拆分行单元格的目标。我可以使用df['fips'] = hello
添加新列并用 填充它hello
。有什么想法吗?
fips row
0 00000 UNITED STATES
1 01000 ALABAMA
2 01001 Autauga County, AL
3 01003 Baldwin County, AL
4 01005 Barbour County, AL
解决方案 1:
TL;DR 版本:
对于简单情况:
我有一个带分隔符的文本列,我想要两列
最简单的解决方案是:
df[['A', 'B']] = df['AB'].str.split(' ', n=1, expand=True)
expand=True
如果字符串的拆分数量不均匀并且您想要None
替换缺失的值,则必须使用。
请注意,无论哪种情况,.tolist()
方法都不是必需的。也不是zip()
。
详细:
Andy Hayden 的解决方案最出色地展示了该方法的威力str.extract()
。
但是对于按已知分隔符进行的简单拆分(例如,按破折号拆分或按空格拆分),该.str.split()
方法就足够了1。它对字符串列(系列)进行操作,并返回列表列(系列):
>>> import pandas as pd
>>> df = pd.DataFrame({'AB': ['A1-B1', 'A2-B2']})
>>> df
AB
0 A1-B1
1 A2-B2
>>> df['AB_split'] = df['AB'].str.split('-')
>>> df
AB AB_split
0 A1-B1 [A1, B1]
1 A2-B2 [A2, B2]
1:如果您不确定的前两个参数的作用,我建议您参阅该方法的纯 Python 版本的.str.split()
文档。
但是你该如何从:
包含双元素列表的列
到:
两列,每列包含列表的相应元素?
好吧,我们需要仔细看看.str
一列的属性。
它是一个神奇的对象,用于收集将列中每个元素视为字符串的方法,然后尽可能高效地在每个元素中应用相应的方法:
>>> upper_lower_df = pd.DataFrame({"U": ["A", "B", "C"]})
>>> upper_lower_df
U
0 A
1 B
2 C
>>> upper_lower_df["L"] = upper_lower_df["U"].str.lower()
>>> upper_lower_df
U L
0 A a
1 B b
2 C c
但它也有一个“索引”接口,用于通过索引获取字符串的每个元素:
>>> df['AB'].str[0]
0 A
1 A
Name: AB, dtype: object
>>> df['AB'].str[1]
0 1
1 2
Name: AB, dtype: object
当然,这个索引接口.str
并不真正关心它索引的每个元素是否实际上是一个字符串,只要它可以被索引就可以了,所以:
>>> df['AB'].str.split('-', 1).str[0]
0 A1
1 A2
Name: AB, dtype: object
>>> df['AB'].str.split('-', 1).str[1]
0 B1
1 B2
Name: AB, dtype: object
然后,利用 Python 元组解包迭代器来做这件事很简单
>>> df['A'], df['B'] = df['AB'].str.split('-', n=1).str
>>> df
AB AB_split A B
0 A1-B1 [A1, B1] A1 B1
1 A2-B2 [A2, B2] A2 B2
当然,通过拆分一列字符串来获取 DataFrame 非常有用,该.str.split()
方法可以使用以下参数为您完成此操作expand=True
:
>>> df['AB'].str.split('-', n=1, expand=True)
0 1
0 A1 B1
1 A2 B2
因此,实现我们想要的目标的另一种方法是:
>>> df = df[['AB']]
>>> df
AB
0 A1-B1
1 A2-B2
>>> df.join(df['AB'].str.split('-', n=1, expand=True).rename(columns={0:'A', 1:'B'}))
AB A B
0 A1-B1 A1 B1
1 A2-B2 A2 B2
该expand=True
版本虽然更长,但与元组解包方法相比具有明显的优势。元组解包不能很好地处理不同长度的拆分:
>>> df = pd.DataFrame({'AB': ['A1-B1', 'A2-B2', 'A3-B3-C3']})
>>> df
AB
0 A1-B1
1 A2-B2
2 A3-B3-C3
>>> df['A'], df['B'], df['C'] = df['AB'].str.split('-')
Traceback (most recent call last):
[...]
ValueError: Length of values does not match length of index
>>>
但通过放置在没有足够“分割”的列中expand=True
可以很好地处理它:None
>>> df.join(
... df['AB'].str.split('-', expand=True).rename(
... columns={0:'A', 1:'B', 2:'C'}
... )
... )
AB A B C
0 A1-B1 A1 B1 None
1 A2-B2 A2 B2 None
2 A3-B3-C3 A3 B3 C3
解决方案 2:
可能有更好的方法,但这是一种方法:
row
0 00000 UNITED STATES
1 01000 ALABAMA
2 01001 Autauga County, AL
3 01003 Baldwin County, AL
4 01005 Barbour County, AL
df = pd.DataFrame(df.row.str.split(' ',1).tolist(),
columns = ['fips','row'])
fips row
0 00000 UNITED STATES
1 01000 ALABAMA
2 01001 Autauga County, AL
3 01003 Baldwin County, AL
4 01005 Barbour County, AL
解决方案 3:
您可以使用正则表达式模式非常巧妙地提取出不同的部分:
In [11]: df.row.str.extract('(?P<fips>d{5})((?P<state>[A-Z ]*$)|(?P<county>.*?), (?P<state_code>[A-Z]{2}$))')
Out[11]:
fips 1 state county state_code
0 00000 UNITED STATES UNITED STATES NaN NaN
1 01000 ALABAMA ALABAMA NaN NaN
2 01001 Autauga County, AL NaN Autauga County AL
3 01003 Baldwin County, AL NaN Baldwin County AL
4 01005 Barbour County, AL NaN Barbour County AL
[5 rows x 5 columns]
解释一下有点长的正则表达式:
(?P<fips>d{5})
匹配五位数字(
d
)并为其命名"fips"
。
下一部分:
((?P<state>[A-Z ]*$)|(?P<county>.*?), (?P<state_code>[A-Z]{2}$))
|
是否执行以下任一操作( ):
(?P<state>[A-Z ]*$)
匹配任意数量 (
*
) 的大写字母或空格 ( ) 并在字符串结尾之前[A-Z ]
命名它( ),"state"
`$`
或者
(?P<county>.*?), (?P<state_code>[A-Z]{2}$))
匹配其他任何内容 (
.*
) 然后逗号和空格
state_code
匹配字符串结尾之前的两位数字($
)。
在示例中:
请注意,前两行命中“州”(在县和 state_code 列中留下 NaN),而后三行命中县、state_code(在州列中留下 NaN)。
解决方案 4:
df[['fips', 'row']] = df['row'].str.split(' ', n=1, expand=True)
解决方案 5:
您可以使用str.split
空格(默认分隔符)和参数expand=True
来DataFrame
分配给新列:
df = pd.DataFrame({'row': ['00000 UNITED STATES', '01000 ALABAMA',
'01001 Autauga County, AL', '01003 Baldwin County, AL',
'01005 Barbour County, AL']})
print (df)
row
0 00000 UNITED STATES
1 01000 ALABAMA
2 01001 Autauga County, AL
3 01003 Baldwin County, AL
4 01005 Barbour County, AL
df[['a','b']] = df['row'].str.split(n=1, expand=True)
print (df)
row a b
0 00000 UNITED STATES 00000 UNITED STATES
1 01000 ALABAMA 01000 ALABAMA
2 01001 Autauga County, AL 01001 Autauga County, AL
3 01003 Baldwin County, AL 01003 Baldwin County, AL
4 01005 Barbour County, AL 01005 Barbour County, AL
如果需要修改,删除原来的列DataFrame.pop
df[['a','b']] = df.pop('row').str.split(n=1, expand=True)
print (df)
a b
0 00000 UNITED STATES
1 01000 ALABAMA
2 01001 Autauga County, AL
3 01003 Baldwin County, AL
4 01005 Barbour County, AL
类似的东西有:
df[['a','b']] = df['row'].str.split(n=1, expand=True)
df = df.drop('row', axis=1)
print (df)
a b
0 00000 UNITED STATES
1 01000 ALABAMA
2 01001 Autauga County, AL
3 01003 Baldwin County, AL
4 01005 Barbour County, AL
如果出现错误:
#remove n=1 for split by all whitespaces
df[['a','b']] = df['row'].str.split(expand=True)
ValueError:列的长度必须与键的长度相同
您可以检查,它返回 4 列DataFrame
,而不仅仅是 2 列:
print (df['row'].str.split(expand=True))
0 1 2 3
0 00000 UNITED STATES None
1 01000 ALABAMA None None
2 01001 Autauga County, AL
3 01003 Baldwin County, AL
4 01005 Barbour County, AL
然后解决方案是DataFrame
通过以下方式添加新的join
:
df = pd.DataFrame({'row': ['00000 UNITED STATES', '01000 ALABAMA',
'01001 Autauga County, AL', '01003 Baldwin County, AL',
'01005 Barbour County, AL'],
'a':range(5)})
print (df)
a row
0 0 00000 UNITED STATES
1 1 01000 ALABAMA
2 2 01001 Autauga County, AL
3 3 01003 Baldwin County, AL
4 4 01005 Barbour County, AL
df = df.join(df['row'].str.split(expand=True))
print (df)
a row 0 1 2 3
0 0 00000 UNITED STATES 00000 UNITED STATES None
1 1 01000 ALABAMA 01000 ALABAMA None None
2 2 01001 Autauga County, AL 01001 Autauga County, AL
3 3 01003 Baldwin County, AL 01003 Baldwin County, AL
4 4 01005 Barbour County, AL 01005 Barbour County, AL
删除原始列(如果还有其他列):
df = df.join(df.pop('row').str.split(expand=True))
print (df)
a 0 1 2 3
0 0 00000 UNITED STATES None
1 1 01000 ALABAMA None None
2 2 01001 Autauga County, AL
3 3 01003 Baldwin County, AL
4 4 01005 Barbour County, AL
解决方案 6:
如果您不想创建新的数据框,或者您的数据框包含的列多于您想要拆分的列,您可以:
df["flips"], df["row_name"] = zip(*df["row"].str.split().tolist())
del df["row"]
解决方案 7:
用于df.assign
创建一个新的 df。请参阅https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.assign.html
split = df_selected['name'].str.split(',', 1, expand=True)
df_split = df_selected.assign(first_name=split[0], last_name=split[1])
df_split.drop('name', 1, inplace=True)
或者以方法链形式:
df_split = (df_selected
.assign(list_col=lambda df: df['name'].str.split(',', 1, expand=False),
first_name=lambda df: df.list_col.str[0],
last_name=lambda df: df.list_col.str[1])
.drop(columns=['list_col']))
解决方案 8:
如果您想根据分隔符将字符串拆分为两列以上,则可以省略“最大拆分”参数。
您可以使用:
df['column_name'].str.split('/', expand=True)
这将自动创建与任何初始字符串中包含的最大字段数一样多的列。
解决方案 9:
很惊讶我还没看过这个。如果你只需要两部,我强烈推荐。。。
Series.str.partition
partition
对分隔符执行一次拆分,并且通常性能很好。
df['row'].str.partition(' ')[[0, 2]]
0 2
0 00000 UNITED STATES
1 01000 ALABAMA
2 01001 Autauga County, AL
3 01003 Baldwin County, AL
4 01005 Barbour County, AL
如果需要重命名行,
df['row'].str.partition(' ')[[0, 2]].rename({0: 'fips', 2: 'row'}, axis=1)
fips row
0 00000 UNITED STATES
1 01000 ALABAMA
2 01001 Autauga County, AL
3 01003 Baldwin County, AL
4 01005 Barbour County, AL
如果您需要将其重新加入到原始内容中,请使用join
或concat
:
df.join(df['row'].str.partition(' ')[[0, 2]])
pd.concat([df, df['row'].str.partition(' ')[[0, 2]]], axis=1)
row 0 2
0 00000 UNITED STATES 00000 UNITED STATES
1 01000 ALABAMA 01000 ALABAMA
2 01001 Autauga County, AL 01001 Autauga County, AL
3 01003 Baldwin County, AL 01003 Baldwin County, AL
4 01005 Barbour County, AL 01005 Barbour County, AL
解决方案 10:
我发现没有人使用过切片方法,因此我在这里提出我的意见。
df["<col_name>"].str.slice(stop=5)
df["<col_name>"].str.slice(start=6)
此方法将创建两个新列。
解决方案 11:
我更喜欢导出对应的 pandas series(也就是我需要的列),使用apply函数将列内容拆分成多个 series,再将生成的列join到现有的 DataFrame 中。当然,源列应该被移除。
例如
col1 = df["<col_name>"].apply(<function>)
col2 = ...
df = df.join(col1.to_frame(name="<name1>"))
df = df.join(col2.toframe(name="<name2>"))
df = df.drop(["<col_name>"], axis=1)
拆分两个单词的字符串函数应该是这样的:
lambda x: x.split(" ")[0] # for the first element
lambda x: x.split(" ")[-1] # for the last element
解决方案 12:
FutureWarning:在 pandas 的未来版本中,StringMethods.split 的所有参数(参数“pat”除外)都将是关键字专用的。
只是对第一个答案的一个小更新。
如果您遇到上面的警告消息(我目前使用的是pd.__version__
= 1.5.3),在未来的版本中会变成错误,您可以通过将参数名称添加到参数中来避免它。因此,LeoRochael 发布的“最简单的解决方案”是:
# Will raise a warning or error
df[['A', 'B']] = df['AB'].str.split(' ', 1, expand=True)
应为:
# For newer pandas versions
df[['A', 'B']] = df['AB'].str.split(' ', n=1, expand=True)
此处发布的其他示例也需要参数的名称。
# For instance, if you want to split and remove the splitted columns
df[['A','B']] = df.pop('AB').str.split(n=1, expand=True)
我希望这能有所帮助。