Jak podzielić kolumnę na dwie kolumny?

197

Mam ramkę danych z jedną kolumną i chciałbym podzielić ją na dwie kolumny, z nagłówkiem jednej kolumny jako „fips' a drugą”'row'

Moja ramka danych dfwygląda następująco:

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

Nie wiem, jak użyć, df.row.str[:]aby osiągnąć cel podziału komórki wiersza. Mogę użyć, df['fips'] = helloaby dodać nową kolumnę i wypełnić ją hello. Jakieś pomysły?

         fips       row
0    00000 UNITED STATES
1    01000 ALABAMA 
2    01001 Autauga County, AL
3    01003 Baldwin County, AL
4    01005 Barbour County, AL
ak
źródło
3
jak załadowałeś swoje dane do pand? Możesz być w stanie laod dane w żądanym formacie za pomocą read_table()lub read_fwf()
Zach

Odpowiedzi:

138

Może być lepszy sposób, ale oto jedno podejście:

                            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
korzeń
źródło
6
Pamiętaj, że .tolist () usunie wszystkie indeksy, które posiadałeś, więc twoja nowa ramka danych zostanie ponownie zindeksowana od 0 (nie ma znaczenia w twoim przypadku).
Crashthatch
10
@ Crashthatch - znowu możesz dodać index = df.indexi jesteś dobry.
root
co jeśli jednej komórki nie można podzielić?
Nisba
@Nisba: Jeśli jakakolwiek komórka nie może zostać podzielona (np. Łańcuch nie zawiera spacji w tym przypadku), nadal będzie działać, ale jedna część podziału będzie pusta. Inne sytuacje się zdarzają, jeśli masz mieszane typy w kolumnie z co najmniej jedną komórką zawierającą dowolny typ liczby. Następnie splitmetoda zwraca NaN, a tolistmetoda zwróci taką wartość, jaką jest (NaN), co spowoduje ValueError(w celu przezwyciężenia tego problemu można przerzucić ją na typ łańcucha przed podziałem). Polecam spróbować samemu, to najlepszy sposób nauki :-)
Nerxis,
@techkuz: Na pewno twój dfma rownagłówek kolumny? Możesz myśleć, że to jakiś atrybut DataFrame, ale jest całkiem jasne, że to nazwa kolumny. To od Ciebie zależy, jak utworzysz i zdefiniujesz nagłówki kolumn, więc jeśli używasz innego, użyj go (np df.my_column_name.split(...).).
Nerxis,
390

Wersja TL; DR:

W prostym przypadku:

  • Mam kolumnę tekstową z separatorem i chcę dwie kolumny

Najprostszym rozwiązaniem jest:

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

Możesz też utworzyć automatycznie DataFrame z jedną kolumną dla każdego wpisu podziału automatycznie za pomocą:

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

Musisz użyć, expand=Truejeśli ciągi mają nierównomierną liczbę podziałów i chcesz Nonezastąpić brakujące wartości.

Zauważ, że w obu przypadkach .tolist()metoda nie jest konieczna. Ani też niezip() .

Szczegółowo:

Rozwiązanie Andy'ego Haydena jest najdoskonalsze w zademonstrowaniu siły str.extract()metody.

Ale w przypadku zwykłego podziału na znany separator (np. Dzielenie za pomocą myślników lub dzielenie za pomocą białych znaków) .str.split()metoda wystarczy 1 . Działa na kolumnie (Seria) ciągów i zwraca kolumnę (Seria) list:

>>> 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: Jeśli nie masz pewności, co robią pierwsze dwa parametry .str.split(), polecam dokumentację dla zwykłej wersji metody w języku Python .

Ale jak to zrobić:

  • kolumna zawierająca listy dwuelementowe

do:

  • dwie kolumny, każda zawierająca odpowiedni element list?

Cóż, musimy przyjrzeć się bliżej .str atrybutowi kolumny.

Jest to magiczny obiekt, który służy do zbierania metod, które traktują każdy element w kolumnie jako ciąg znaków, a następnie stosuje odpowiednią metodę w każdym elemencie tak wydajnie, jak to możliwe:

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

Ale ma również interfejs „indeksowania” do pobierania każdego elementu ciągu według jego indeksu:

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

0    A
1    A
Name: AB, dtype: object

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

0    1
1    2
Name: AB, dtype: object

Oczywiście ten interfejs indeksowania .strnie obchodzi, czy każdy indeksowany element jest w rzeczywistości łańcuchem, o ile można go indeksować, więc:

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

Następnie jest to prosta kwestia skorzystania z rozpakowywania iterowych krotek w Pythonie

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

Oczywiście uzyskanie DataFrame z podziału kolumny ciągów jest tak przydatne, że .str.split()metoda może to zrobić za pomocą expand=Trueparametru:

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

    0   1
0  A1  B1
1  A2  B2

Kolejnym sposobem na osiągnięcie tego, co chcieliśmy, jest:

>>> 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=TrueWersja, chociaż dłużej, ma wyraźną przewagę nad metodą krotki rozpakowaniu. Rozpakowywanie krotek nie radzi sobie dobrze z podziałami o różnych długościach:

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

Ale expand=Trueradzi sobie z tym ładnie, umieszczając Nonew kolumnach, dla których nie ma wystarczającej liczby „podziałów”:

>>> 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
źródło
df ['A'], df ['B'] = df ['AB']. str. split ('', 1) .str Jakie jest znaczenie '1' w split ('', 1)?
Hariprasad
@Hariprasad, to maksymalna liczba podziałów. Dodałem link do dokumentacji dla wersji Pythona .split()metody wyjaśniającej dwa pierwsze parametry lepiej niż dokumenty Pandy.
LeoRochael
5
raporty pandas 1.0.0 „FutureWarning: W kolejnych wydaniach iteracja kolumnowa nad postaciami będzie przestarzała”.
Frank
1
Działa to w Pythonie 1.0.1. df.join(df['AB'].str.split('-', 1, expand=True).rename(columns={0:'A', 1:'B'}))
Martien Lubberink
59

Możesz dokładnie wyodrębnić różne części za pomocą wzoru regularnego:

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]

Aby wyjaśnić nieco długi regex:

(?P<fips>\d{5})
  • Dopasowuje pięć cyfr ( \d) i nazywa je "fips".

Następna część:

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

Czy albo ( |) jedna z dwóch rzeczy:

(?P<state>[A-Z ]*$)
  • Pasuje do dowolnej liczby ( *) wielkich liter lub spacji ( [A-Z ]) i nazywa ją "state"przed końcem ciągu ( $),

lub

(?P<county>.*?), (?P<state_code>[A-Z]{2}$))
  • dopasowuje cokolwiek innego ( .*)
  • przecinek i spacja
  • dopasowuje dwie cyfry state_codeprzed końcem ciągu ( $).

W przykładzie:
Zauważ, że pierwsze dwa wiersze uderzyły w „stan” (pozostawiając NaN w kolumnie hrabstwa i state_code), podczas gdy trzy ostatnie uderzyły w hrabstwo, state_code (pozostawiając NaN w kolumnie stanu).

Andy Hayden
źródło
Jest to zdecydowanie najlepsze rozwiązanie, ale dla niektórych może być nieco przytłaczające z powodu bardzo obszernego wyrażenia regularnego. Dlaczego nie zrobić tego jako część 2 i mieć część 1 z tylko fips i kolumnami wierszy?
Małe Stoły Bobby'ego
2
@ josh to dobra uwaga, podczas gdy poszczególne części wyrażenia regularnego są „łatwe” do zrozumienia, długie wyrażenia regularne mogą szybko się skomplikować. Dodałem wyjaśnienia dla przyszłych czytelników! (Musiałem również zaktualizować link do dokumentacji, która wyjaśnia (?P<label>...)składnię! Nie mam pojęcia, dlaczego wybrałem bardziej złożony regex, najwyraźniej prosty może działać hmmmm
Andy Hayden
1
Wygląda o wiele bardziej przyjaźnie. Cieszę się, że to zrobiłeś, ponieważ dzięki niemu spojrzałem na dokumenty, aby zrozumieć <group_name>. Teraz wiem, że dzięki temu mój kod jest bardzo zwięzły.
Małe tabele Bobby'ego,
44
df[['fips', 'row']] = df['row'].str.split(' ', n=1, expand=True)
Bhagabat Behera
źródło
23

Jeśli nie chcesz tworzyć nowej ramki danych lub jeśli twoja rama danych ma więcej kolumn niż tylko te, które chcesz podzielić, możesz:

df["flips"], df["row_name"] = zip(*df["row"].str.split().tolist())
del df["row"]  
keberwein
źródło
1
Pojawia się zip argument #1 must support iterationbłąd, python 2.7
Allan Ruin
20

Można użyć str.splitspacjami (domyślnie) i separatora parametr expand=Truedla DataFramez przypisać do nowych kolumn:

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

W razie potrzeby zmień oryginalną kolumnę za pomocą 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

Jak to jest:

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

Jeśli pojawi się błąd:

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

ValueError: Kolumny muszą mieć tę samą długość co klucz

Możesz sprawdzić i zwraca 4 kolumny DataFrame, nie tylko 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

Następnie dodawane jest nowe rozwiązanie DataFrameprzez 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

Z usuń oryginalną kolumnę (jeśli są jeszcze inne kolumny):

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
źródło
8

Jeśli chcesz podzielić ciąg na więcej niż dwie kolumny oparte na ograniczniku, możesz pominąć parametr „maksymalne podziały”.
Możesz użyć:

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

Spowoduje to automatyczne utworzenie tylu kolumn, ile wynosi maksymalna liczba pól zawartych w dowolnym z początkowych ciągów.

Jaśmin
źródło
6

Zaskoczony, jeszcze go nie widziałem. Jeśli potrzebujesz tylko dwóch podziałów, bardzo polecam. . .

Series.str.partition

partition wykonuje jeden podział na separatorze i ogólnie jest dość wydajny.

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

Jeśli musisz zmienić nazwę wierszy,

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

Jeśli chcesz dołączyć to z powrotem do oryginału, użyj joinlub 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
źródło
0

Wolę eksportu odpowiednią serią pandy (czyli kolumn muszę), stosując zastosować funkcję podzielić zawartość kolumny na wiele serii, a następnie dołączyć wygenerowane kolumny do istniejącego DataFrame. Oczywiście kolumna źródłowa powinna zostać usunięta.

na przykład

 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)

Aby podzielić dwa słowa, funkcja ciągów znaków powinna być taka:

lambda x: x.split(" ")[0] # for the first element
lambda x: x.split(" ")[-1] # for the last element
Mcchran
źródło
0

Widziałem, że nikt nie zastosował metody wycinania, więc tutaj umieściłem tutaj moje 2 centy.

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

Ta metoda utworzy dwie nowe kolumny.

Vingt Cent
źródło