如何把一个数据帧字符串列分成两列?

我有一个数据帧有一个(字符串)列,我想把它分成两个(字符串)列,其中一个列标头为'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
716036 次浏览

也许有更好的方法,但这是一种方法:

                            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

你可以提取不同的部分非常整齐地使用正则表达式模式:

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
< p > 在这个例子中: < br > 请注意,前两行命中了“state”(将NaN留在county和state_code列中),而最后三行命中了county, state_code(将NaN留在state列中)

如果你不想创建一个新的数据框架,或者你的数据框架有更多的列,而不仅仅是你想要分割的列,你可以:

df["flips"], df["row_name"] = zip(*df["row"].str.split().tolist())
del df["row"]

TL;博士版:

对于简单的情况:

  • 我有一个文本列与分隔符,我想要两列

最简单的解决方案是:

df[['A', 'B']] = df['AB'].str.split(' ', 1, expand=True)

如果字符串的分隔数不一致,并且希望用None替换缺失的值,则必须使用expand=True

注意,在这两种情况下,.tolist()方法都是不必要的。zip()也不是。

详细:

安迪·海登的解决方案在演示str.extract()方法的功能方面是最出色的。

但是对于一个已知分隔符的简单分割(比如,用破折号分割,或者用空格分割),.str.split()方法就足够了。它操作一个字符串列(Series),并返回一个列表列(Series):

>>> 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:如果你不确定.str.split()的前两个参数是做什么的, 我推荐该方法的普通Python版本.

. doc

但是你如何从:

  • 包含两元素列表的列

:

  • 两列,每个列都包含列表的各自元素?

好吧,我们需要仔细看看列的.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('-', 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('-', 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('-', 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

如果你想根据一个分隔符将一个字符串分割成两个以上的列,你可以省略'maximum splits'参数 你可以使用:

df['column_name'].str.split('/', expand=True)

这将自动创建与任何初始字符串中包含的最大字段数量相同的列。

你可以通过空格(默认分隔符)使用str.split,并为DataFrame使用参数expand=True赋值给新列:

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

如果get错误:

#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

那么解决方案是通过join追加新的DataFrame:

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

我更喜欢导出相应的pandas系列(即我需要的列),使用应用函数将列内容分割为多个系列,然后加入生成的列到现有的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
df[['fips', 'row']] = df['row'].str.split(' ', n=1, expand=True)

没想到我还没见过这个。如果你只需要两个分割,我强烈推荐…

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

如果你需要将它连接回原来的,使用joinconcat:

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

我发现没人用切片法,所以我把2美分写在这里。

df["<col_name>"].str.slice(stop=5)
df["<col_name>"].str.slice(start=6)

该方法将创建两个新列。

使用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']))