Как разбить столбец на два столбца?

197

У меня есть фрейм данных с одним столбцом, и я хотел бы разбить его на два столбца, с одним заголовком столбца как ', 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
Аляска
источник
3
как ты загрузил свои данные в панд? Возможно, вы сможете разместить данные в нужном вам формате, используя read_table()или read_fwf()
zach

Ответы:

140

Возможно, есть лучший способ, но вот один из подходов:

                            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 = ['flips','row'])
   flips                 row
0  00000       UNITED STATES
1  01000             ALABAMA
2  01001  Autauga County, AL
3  01003  Baldwin County, AL
4  01005  Barbour County, AL
корень
источник
6
Имейте в виду, что .tolist () удалит все ваши индексы, поэтому ваш новый Dataframe будет переиндексирован с 0 (это не имеет значения в вашем конкретном случае).
Crashthatch
10
@Crashthatch - опять же, вы можете просто добавить, index = df.indexи вы хорошо.
root
Что делать, если одну клетку нельзя разделить?
Нисба
@Nisba: Если ни одна ячейка не может быть разделена (например, строка не содержит пробелов для этого случая), она все равно будет работать, но одна часть разделения будет пустой. Другие ситуации могут возникнуть, если в столбце смешанные типы, хотя бы одна ячейка содержит любой числовой тип. Затем splitметод возвращает NaN, и tolistметод вернет это значение как есть (NaN), что приведет к ValueError(чтобы преодолеть эту проблему, вы можете привести его к строковому типу перед разделением). Я рекомендую вам попробовать это самостоятельно, это лучший способ обучения :-)
Nerxis
@techkuz: Вы уверены, что у вас dfесть rowзаголовок столбца? Вы можете подумать, что это какой-то атрибут DataFrame, но совершенно ясно, что это имя столбца. Это зависит от вас, как вы создаете и определяете заголовки столбцов, поэтому, если вы используете другой, используйте его (например df.my_column_name.split(...)).
Nerxis
392

TL; версия DR:

Для простого случая:

  • У меня есть текстовый столбец с разделителем, и я хочу два столбца

Самое простое решение:

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

Или вы можете автоматически создать DataFrame с одним столбцом для каждой записи разделения:

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

Вы должны использовать, expand=Trueесли ваши строки имеют неоднородное количество разбиений, и вы хотите Noneзаменить отсутствующие значения.

Обратите внимание, что в любом случае .tolist()метод не является обязательным. Ни то, ни другое zip().

В деталях:

Решение Энди Хейдена является превосходным в демонстрации силы str.extract()метода.

Но для простого разбиения по известному разделителю (например, деление по тире или расщепление по пробелам) этого .str.split()метода достаточно 1 . Он работает со столбцом (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-версии метода .

Но как вы идете от:

  • столбец, содержащий двухэлементные списки

чтобы:

  • два столбца, каждый из которых содержит соответствующий элемент списков?

Что ж, нам нужно поближе взглянуть на .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
LeoRochael
источник
df ['A'], df ['B'] = df ['AB']. str.split ('', 1) .str Что означает «1» в split ('', 1)?
Харипрасад
@Hariprasad, это максимальное количество расколов. Я добавил ссылку на документы для Python-версии .split()метода, которая объясняет первые два параметра лучше, чем документы Pandas.
LeoRochael
5
Pandas 1.0.0 сообщает: «FutureWarning: итерация столбцов над символами будет устаревшей в будущих выпусках».
Фрэнк
1
Это работает под Python 1.0.1. df.join(df['AB'].str.split('-', 1, expand=True).rename(columns={0:'A', 1:'B'}))
Мартиен Лубберинк
59

Вы можете аккуратно извлечь разные части, используя шаблон регулярных выражений:

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до конца строки ( $).

В примере:
обратите внимание, что первые две строки попадают в «состояние» (оставляя NaN в столбцах county и state_code), в то время как последние три строки попадают в округ, state_code (оставляя NaN в столбце state).

Энди Хейден
источник
Это определенно лучшее решение, но это может быть немного ошеломляющим для некоторых с очень обширным регулярным выражением. Почему бы не сделать это как часть 2 и иметь часть 1 только со столбцами fips и row?
Маленькие столики Бобби
2
@ Джош, это хорошее замечание, хотя отдельные части регулярного выражения «легки» для понимания, длинное регулярное выражение может быстро усложниться. Я добавил некоторые объяснения для будущих читателей! (Мне также пришлось обновить ссылку на документы, которая объясняет (?P<label>...)синтаксис! Я понятия не имею, почему я выбрал более сложное регулярное выражение, очевидно, что простое могло бы работать хммммм
Энди Хейден,
1
Выглядит намного дружелюбнее. Я рад, что вы сделали, потому что это заставило меня взглянуть на документы, чтобы понять <group_name>. Теперь я знаю, что это делает мой код очень лаконичным.
Маленькие столики Бобби
44
df[['fips', 'row']] = df['row'].str.split(' ', n=1, expand=True)
Бхагабад Бехера
источник
23

Если вы не хотите создавать новый фрейм данных или если ваш фрейм данных содержит больше столбцов, чем просто те, которые вы хотите разделить, вы можете:

df["flips"], df["row_name"] = zip(*df["row"].str.split().tolist())
del df["row"]  
keberwein
источник
1
Я получаю сообщение zip argument #1 must support iterationоб ошибке, Python 2.7
Аллан Руин
20

Вы можете использовать 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   
jezrael
источник
8

Если вы хотите разбить строку на более чем два столбца на основе разделителя, вы можете пропустить параметр 'Maximum Split'.
Ты можешь использовать:

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

Это автоматически создаст столько столбцов, сколько максимальное количество полей включено в любую из ваших исходных строк.

жасмин
источник
6

Удивлен, я еще не видел этого. Если вам нужно только два сплита, я очень рекомендую. , ,

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
cs95
источник
0

Я предпочитаю экспортировать соответствующие серии панд (т.е. нужные мне столбцы), используя функцию apply, чтобы разбить содержимое столбцов на несколько серий, а затем соединить созданные столбцы с существующим 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
mcchran
источник
0

Я видел, что никто не использовал метод среза, поэтому здесь я положил свои 2 цента здесь.

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

Этот метод создаст два новых столбца.

Вингт Цент
источник