【问题标题】:How to split a dataframe string column into two columns?如何将数据框字符串列拆分为两列?
【发布时间】:2013-01-22 14:13:40
【问题描述】:

我有一个包含一列(字符串)的数据框,我想将其拆分为两列(字符串),一列标题为 '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

【问题讨论】:

  • 您是如何将数据加载到 pandas 中的?您也许可以使用 read_table()read_fwf() 以您想要的格式加载数据
  • "How to split a column" 有不同的答案,具体取决于该列是字符串、列表还是其他内容,以及什么格式(例如“格式化字符串”,如地址,您可能需要使用正则表达式。这里有一个固定宽度格式的字符串列(“ZZZZZ 地名...”),所以我们知道邮政编码是字符 0:4,地名是字符 6:

标签: python dataframe pandas


【解决方案1】:

可能有更好的方法,但这是一种方法:

                            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

【讨论】:

  • 请注意 .tolist() 将删除您拥有的所有索引,因此您的新 Dataframe 将从 0 重新索引(在您的具体情况下无关紧要)。
  • @Crashthatch -- 然后你可以再次添加index = df.index,你很好。
  • 一个单元格不能拆分怎么办?
  • @Nisba:如果任何单元格无法拆分(例如,在这种情况下字符串不包含任何空格),它仍然可以工作,但拆分的一部分将为空。如果您在列中混合了类型,并且至少一个单元格包含任何数字类型,则会发生其他情况。然后split 方法返回NaN,tolist 方法将按原样(NaN)返回此值,这将导致ValueError(要克服此问题,您可以在拆分之前将其转换为字符串类型)。我建议您自己尝试,这是最好的学习方式:-)
  • @techkuz:你确定你的dfrow 列标题吗?您可能认为这是某种 DataFrame 属性,但很明显这是列的名称。如何创建和定义列标题取决于您,因此如果您使用不同的标题,请使用它(例如df.my_column_name.split(...))。
【解决方案2】:

您可以使用正则表达式模式将extract 的不同部分巧妙地分开:

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(在 state 列中保留 NaN)。

【讨论】:

  • 这绝对是最好的解决方案,但对于一些使用非常广泛的正则表达式的人来说可能有点不知所措。为什么不把它作为第 2 部分,让第 1 部分只包含 fips 和 row 列?
  • @josh 很好,虽然正则表达式的各个部分“容易”理解,但长正则表达式很快就会变得复杂。我为未来的读者添加了一些解释! (我还必须更新解释(?P&lt;label&gt;...) 语法的文档的链接!我不知道为什么我要使用更复杂的正则表达式,显然简单的可以工作hmmmm
  • 看起来更友好。我很高兴你这样做了,因为它让我查看了文档以了解 &lt;group_name&gt;。现在我知道它使我的代码非常简洁。
【解决方案3】:

如果您不想创建新的数据框,或者您的数据框的列多于您要拆分的列,您可以:

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

【讨论】:

  • 我收到zip argument #1 must support iteration 错误,python 2.7
【解决方案4】:

TL;DR 版本:

对于以下简单情况:

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

最简单的解决方案是:

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

如果您的字符串具有不均匀的拆分数量并且您希望None 替换缺失值,则必须使用expand=True

请注意,无论哪种情况,.tolist() 方法都不是必需的。 zip() 也不是。

详细说明:

Andy Hayden's solution 最出色地展示了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: 如果你不确定.str.split() 的前两个参数是做什么的, 我推荐plain Python version of the method 的文档。

但是你怎么去:

  • 包含两个元素列表的列

到:

  • 两列,每列包含列表的相应元素?

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

【讨论】:

  • df['A'], df['B'] = df['AB'].str.split(' ', 1).str split中的'1'是什么意思(' ', 1) ?
  • pandas 1.0.0 报告“FutureWarning:字符的列迭代将在未来的版本中被弃用。”
  • 这适用于 Python 1.0.1。 df.join(df['AB'].str.split('-', 1, expand=True).rename(columns={0:'A', 1:'B'}))
  • @DataGirl,为此我建议Series.str.extract 使用适当的正则表达式。
  • @JohnVandivier,您的拆分列中的任何值都可能没有达到您期望的拆分数量,因此拆分操作不会产生您尝试分配的列数。检查this pandas bug,也许还有this other S.O. question。我会尝试想办法扩展这个答案以克服这个问题,或者至少警告它。
【解决方案5】:

如果您想根据分隔符将字符串拆分为多于两列,您可以省略“最大拆分数”参数。
你可以使用:

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

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

【讨论】:

    【解决方案6】:

    您可以将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
    

    然后解决方案是通过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   
    

    【讨论】:

    • 如果有两个这样的列,需要拆分,则返回:ValueError: columns overlap but no suffix specified:
    【解决方案7】:

    我更喜欢导出对应的pandas系列(即我需要的列),使用apply函数将列内容拆分成多个系列,然后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
    

    【讨论】:

      【解决方案8】:
      df[['fips', 'row']] = df['row'].str.split(' ', n=1, 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
        

        如果您需要将其加入到原始状态,请使用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
        

        【讨论】:

          【解决方案10】:

          我看到没有人用过切片方法,所以我把我的 2 美分放在这里。

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

          此方法将创建两个新列。

          【讨论】:

            【解决方案11】:

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

            【讨论】:

              猜你喜欢
              相关资源
              最近更新 更多