Como dividir uma coluna em duas colunas?


196

Eu tenho um quadro de dados com uma coluna e gostaria de dividi-lo em duas colunas, com um cabeçalho de coluna como ' fips'e o outro'row'

Meu dataframe dffica assim:

          row
0    00000 UNITED STATES
1    01000 ALABAMA
2    01001 Autauga County, AL
3    01003 Baldwin County, AL
4    01005 Barbour County, AL

Não sei como usar df.row.str[:]para atingir meu objetivo de dividir a célula da linha. Eu posso usar df['fips'] = hellopara adicionar uma nova coluna e preenchê-la hello. Alguma ideia?

         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
como você carregou seus dados nos pandas? Você pode ser capaz de laod os dados em seu formato desejado usando read_table()ou read_fwf()
Zach

Respostas:


137

Pode haver uma maneira melhor, mas esta é uma abordagem:

                            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
Esteja ciente de que .tolist () removerá todos os índices que você tinha, portanto seu novo Dataframe será reindexado de 0 (não importa no seu caso específico).
Crashthatch 27/03

10
@ Baterhthatch - então, novamente, você pode simplesmente adicionar index = df.indexe você é bom.
root

e se uma célula não puder ser dividida?
Nisba

@ Nisba: Se alguma célula não puder ser dividida (por exemplo, a string não contém espaço para este caso), ela ainda funcionará, mas uma parte da divisão estará vazia. Outras situações ocorrerão caso você tenha tipos mistos na coluna com pelo menos uma célula contendo qualquer tipo de número. Em seguida, o splitmétodo retorna NaN e o tolistmétodo retornará esse valor como está (NaN), o que resultará em ValueError(para superar esse problema, você pode convertê-lo no tipo de string antes de dividir). Eu recomendo que você experimentá-lo em seu próprio país é a melhor forma de aprender :-)
Nerxis

@techkuz: Você tem certeza de que dfpossui o rowcabeçalho da coluna? Você pode pensar que é algum tipo de atributo DataFrame, mas é bem claro que esse é o nome da coluna. Depende de você como criar e definir os cabeçalhos das colunas; portanto, se você usar um diferente, use-o (por exemplo df.my_column_name.split(...)).
Nerxis

388

Versão TL; DR:

Para o caso simples de:

  • Eu tenho uma coluna de texto com um delimitador e quero duas colunas

A solução mais simples é:

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

Ou você pode criar um DataFrame com uma coluna para cada entrada da divisão automaticamente com:

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

Você deve usar expand=Truese suas seqüências de caracteres tiverem um número não uniforme de divisões e desejar Nonesubstituir os valores ausentes.

Observe como, em ambos os casos, o .tolist()método não é necessário. Nem é zip().

Em detalhe:

A solução de Andy Hayden é mais excelente para demonstrar o poder do str.extract()método.

Mas, para uma simples divisão sobre um separador conhecido (como, por hífens ou em branco), o .str.split()método é suficiente 1 . Opera em uma coluna (Série) de cadeias e retorna uma coluna (Série) de listas:

>>> 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: Se você não tiver certeza do que os dois primeiros parâmetros .str.split()fazem, recomendo os documentos para a versão simples do método Python .

Mas como você vai:

  • uma coluna contendo listas de dois elementos

para:

  • duas colunas, cada uma contendo o respectivo elemento das listas?

Bem, precisamos dar uma olhada mais de perto no .str atributo de uma coluna.

É um objeto mágico que é usado para coletar métodos que tratam cada elemento em uma coluna como uma sequência e, em seguida, aplica o método respectivo em cada elemento da maneira mais eficiente possível:

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

Mas também possui uma interface de "indexação" para obter cada elemento de uma string pelo seu índice:

>>> df['AB'].str[0]

0    A
1    A
Name: AB, dtype: object

>>> df['AB'].str[1]

0    1
1    2
Name: AB, dtype: object

Obviamente, essa interface de indexação .strnão se importa realmente se cada elemento que está sendo indexado é realmente uma string, desde que possa ser indexado, portanto:

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

Então, é uma simples questão de tirar vantagem da descompactação tupla do Python dos iteráveis

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

Obviamente, tirar um DataFrame da divisão de uma coluna de strings é tão útil que o .str.split()método pode fazer isso por você com o expand=Trueparâmetro:

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

    0   1
0  A1  B1
1  A2  B2

Portanto, outra maneira de realizar o que queríamos é fazer:

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

A expand=Trueversão, embora mais longa, possui uma vantagem distinta sobre o método de descompactação da tupla. A descompactação da tupla não lida bem com divisões de diferentes comprimentos:

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

Mas expand=Truelida bem com isso, colocando Nonenas colunas para as quais não há "divisões" suficientes:

>>> 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 Qual é o significado de '1' na divisão ('', 1)?
Hariprasad 30/03

@Hariprasad, é o número máximo de divisões. Adicionei um link para os documentos da versão Python do .split()método, que explicam os dois primeiros parâmetros melhor do que os documentos do Pandas.
LeoRochael

5
pandas 1.0.0 reporta "FutureWarning: iteração em colunas sobre caracteres será descontinuada em versões futuras."
Frank

1
Isso funciona no Python 1.0.1. df.join(df['AB'].str.split('-', 1, expand=True).rename(columns={0:'A', 1:'B'}))
Martien Lubberink

59

Você pode extrair as diferentes partes com bastante nitidez usando um padrão regex:

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]

Para explicar a regex um tanto longa:

(?P<fips>\d{5})
  • Corresponde aos cinco dígitos ( \d) e os nomeia"fips" .

A próxima parte:

((?P<state>[A-Z ]*$)|(?P<county>.*?), (?P<state_code>[A-Z]{2}$))

Faz ( |) uma das duas coisas:

(?P<state>[A-Z ]*$)
  • Corresponde a qualquer número ( *) de letras maiúsculas ou espaços ( [A-Z ]) e nomeia isso "state"antes do final da sequência ( $),

ou

(?P<county>.*?), (?P<state_code>[A-Z]{2}$))
  • corresponde a qualquer outra coisa (.* )
  • uma vírgula e um espaço então
  • corresponde aos dois dígitos state_codeantes do final da string ( $).

No exemplo:
Observe que as duas primeiras linhas atingem o "estado" (deixando o NaN nas colunas county e state_code), enquanto as três últimas atingem o município, state_code (deixando o NaN na coluna state).


Essa é definitivamente a melhor solução, mas pode ser um pouco esmagadora para alguns com a extensa regex. Por que não fazer isso como parte 2 e ter a parte 1 com apenas os colunas fips e row?
Tabelas Pouco Bobby

2
@josh esse é um bom argumento, enquanto as partes individuais do regex são "fáceis" de entender, o regex longo pode se complicar rapidamente. Eu adicionei algumas explicações para futuros leitores! (Eu também tive que atualizar o link para os documentos, o que explica a (?P<label>...)sintaxe! Eu não tenho idéia do porquê de eu ter optado pelo regex mais complexo, claramente o mais simples poderia funcionar
Andy Hayden

1
Parece muito mais amigável. Estou feliz que você fez, porque me fez olhar para os documentos para entender o <group_name>. Agora eu sei que isso torna meu código muito sucinto.
Tabelas Pouco Bobby


22

Se você não deseja criar um novo quadro de dados ou se o seu quadro de dados tiver mais colunas do que as que você deseja dividir, você poderá:

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

1
Eu recebo um zip argument #1 must support iterationerro, python 2.7
Allan Ruin

20

Você pode usar str.splitpor espaço em branco (separador padrão) e parâmetro expand=Truepara DataFrameatribuir a novas colunas:

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

Modificação, se necessário, remover a coluna original com 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

Como é o mesmo:

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

Se receber erro:

#remove n=1 for split by all whitespaces
df[['a','b']] = df['row'].str.split(expand=True)

ValueError: as colunas devem ter o mesmo comprimento que a chave

Você pode verificar e retornar 4 colunas DataFrame, não apenas 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

Em seguida, a solução é anexada nova DataFramepor 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

Com remover coluna original (se houver também outras colunas):

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   

8

Se você deseja dividir uma string em mais de duas colunas com base em um delimitador, pode omitir o parâmetro 'maximum splits'.
Você pode usar:

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

Isso criará automaticamente tantas colunas quanto o número máximo de campos incluídos em qualquer uma das suas seqüências iniciais.


6

Surpreso, ainda não vi esse. Se você precisar apenas de duas divisões, eu recomendo. . .

Series.str.partition

partition realiza uma divisão no separador e geralmente tem um bom desempenho.

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

Se você precisar renomear as linhas,

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

Se você precisar associá-lo ao original, use joinou 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

0

Prefiro exportar a série correspondente dos pandas (ou seja, as colunas necessárias), usando a função aplicar para dividir o conteúdo da coluna em várias séries e depois associar as colunas geradas ao DataFrame existente. Obviamente, a coluna de origem deve ser removida.

por exemplo

 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)

Para dividir duas palavras, a função de strings deve ser algo assim:

lambda x: x.split(" ")[0] # for the first element
lambda x: x.split(" ")[-1] # for the last element

0

Vi que ninguém havia usado o método das fatias, então aqui coloquei meus 2 centavos aqui.

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

Este método criará duas novas colunas.


Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.