Wie teile ich eine Spalte in zwei Spalten auf?


196

Ich habe einen Datenrahmen mit einer Spalte und möchte ihn in zwei Spalten aufteilen, wobei eine Spaltenüberschrift ' fips'und die andere ' lautet'row'

Mein Datenrahmen dfsieht folgendermaßen aus:

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

Ich weiß nicht, wie ich df.row.str[:]mein Ziel, die Zeilenzelle zu teilen, erreichen soll. Ich kann verwenden df['fips'] = hello, um eine neue Spalte hinzuzufügen und sie mit zu füllen hello. Irgendwelche Ideen?

         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
Wie haben Sie Ihre Daten in Pandas geladen? Möglicherweise können Sie die Daten in Ihrem gewünschten Format mit read_table()oder read_fwf()
zach

Antworten:


136

Es könnte einen besseren Weg geben, aber dies ist ein Ansatz:

                            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
Beachten Sie, dass .tolist () alle Indizes entfernt, die Sie hatten, sodass Ihr neuer Datenrahmen von 0 neu indiziert wird (dies spielt in Ihrem speziellen Fall keine Rolle).
Crashthatch

10
@ Crashthatch - dann kannst du einfach hinzufügen index = df.indexund du bist gut.
Wurzel

Was ist, wenn eine Zelle nicht geteilt werden kann?
Nisba

@Nisba: Wenn eine Zelle nicht geteilt werden kann (z. B. enthält die Zeichenfolge in diesem Fall keinen Platz), funktioniert sie weiterhin, aber ein Teil der Teilung ist leer. Andere Situationen treten auf, wenn Sie gemischte Typen in der Spalte mit mindestens einer Zelle haben, die einen beliebigen Zahlentyp enthält. Dann gibt die splitMethode NaN zurück und die tolistMethode gibt diesen Wert unverändert zurück (NaN), was dazu führt ValueError(um dieses Problem zu beheben, können Sie ihn vor dem Teilen in einen Zeichenfolgentyp umwandeln). Ich empfehle Ihnen, es selbst zu versuchen, es ist die beste Art zu lernen :-)
Nerxis

@techkuz: Bist du sicher, dass du dfdie rowSpaltenüberschrift hast ? Sie mögen denken, es ist eine Art DataFrame-Attribut, aber es ist ziemlich klar, dass dies der Name der Spalte ist. Es liegt an Ihnen, wie Sie Ihre Spaltenüberschriften erstellen und definieren. Wenn Sie also eine andere verwenden, verwenden Sie diese (z df.my_column_name.split(...). B. ).
Nerxis

388

TL; DR-Version:

Für den einfachen Fall von:

  • Ich habe eine Textspalte mit einem Trennzeichen und möchte zwei Spalten

Die einfachste Lösung ist:

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

Oder Sie können einen DataFrame mit einer Spalte für jeden Eintrag des Split automatisch erstellen mit:

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

Sie müssen verwenden, expand=Truewenn Ihre Zeichenfolgen eine ungleichmäßige Anzahl von Teilungen aufweisen und Sie Nonedie fehlenden Werte ersetzen möchten .

Beachten Sie, dass die .tolist()Methode in beiden Fällen nicht erforderlich ist. Weder ist zip().

Im Detail:

Die Lösung von Andy Hayden ist hervorragend geeignet, um die Leistungsfähigkeit der str.extract()Methode zu demonstrieren .

Für eine einfache Aufteilung über ein bekanntes Trennzeichen (z. B. Aufteilen durch Bindestriche oder Aufteilen durch Leerzeichen) reicht die .str.split()Methode jedoch aus 1 . Es verarbeitet eine Spalte (Serie) von Zeichenfolgen und gibt eine Spalte (Serie) von Listen zurück:

>>> 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: Wenn Sie sich nicht sicher sind, was die ersten beiden Parameter .str.split()tun, empfehle ich die Dokumente für die einfache Python-Version der Methode .

Aber wie geht es dir:

  • eine Spalte mit Listen mit zwei Elementen

zu:

  • zwei Spalten, die jeweils das jeweilige Element der Listen enthalten?

Nun, wir müssen uns das .strAttribut einer Spalte genauer ansehen .

Es ist ein magisches Objekt, mit dem Methoden gesammelt werden, die jedes Element in einer Spalte als Zeichenfolge behandeln und dann die entsprechende Methode in jedem Element so effizient wie möglich anwenden:

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

Es hat aber auch eine "Indizierungs" -Schnittstelle, um jedes Element eines Strings anhand seines Index abzurufen:

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

0    A
1    A
Name: AB, dtype: object

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

0    1
1    2
Name: AB, dtype: object

Natürlich ist es dieser Indizierungsschnittstelle von .stregal, ob jedes zu indizierende Element tatsächlich eine Zeichenfolge ist, solange es indiziert werden kann.

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

Dann ist es eine einfache Sache, das Python-Tupel zu nutzen, um iterables zu entpacken

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

Natürlich ist es so nützlich, einen DataFrame aus dem Teilen einer Zeichenfolgenspalte herauszuholen, dass die .str.split()Methode dies mit dem folgenden expand=TrueParameter für Sie tun kann:

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

    0   1
0  A1  B1
1  A2  B2

Ein anderer Weg, um das zu erreichen, was wir wollten, ist:

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

Die expand=TrueVersion ist zwar länger, hat aber einen deutlichen Vorteil gegenüber der Tupel-Entpackmethode. Das Auspacken von Tupeln eignet sich nicht für Splits unterschiedlicher Länge:

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

Aber expand=Truees geht gut, indem man Nonein die Spalten setzt, für die es nicht genug "Splits" gibt:

>>> 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 Was bedeutet '1' in split ('', 1)?
Hariprasad

@ Hariprasad, es ist die maximale Anzahl von Teilungen. Ich habe einen Link zu den Dokumenten für die Python-Version der .split()Methode hinzugefügt, der die ersten beiden Parameter besser erklärt als die Pandas-Dokumente.
LeoRochael

5
pandas 1.0.0 berichtet "FutureWarning: Spalteniteration über Zeichen wird in zukünftigen Versionen nicht mehr unterstützt."
Frank

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

59

Sie können die verschiedenen Teile mit einem Regex-Muster ganz ordentlich extrahieren :

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]

Um den etwas langen regulären Ausdruck zu erklären:

(?P<fips>\d{5})
  • Entspricht den fünf Ziffern ( \d) und benennt sie "fips".

Der nächste Teil:

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

Hat entweder ( |) eines von zwei Dingen:

(?P<state>[A-Z ]*$)
  • Entspricht einer beliebigen Zahl (* ) von Großbuchstaben oder Leerzeichen ( [A-Z ]) und benennt diese "state"vor dem Ende der Zeichenfolge ( $).

oder

(?P<county>.*?), (?P<state_code>[A-Z]{2}$))
  • passt dann zu allem anderen ( .*)
  • dann ein Komma und ein Leerzeichen
  • stimmt mit der zweistelligen Zahl state_codevor dem Ende der Zeichenfolge überein ( $).

Im Beispiel:
Beachten Sie, dass die ersten beiden Zeilen den Status "State" (NaN in den Spalten "County" und "State_Code") und die letzten drei Zeilen "State_code" (NaN in der Spalte "State") treffen.


Dies ist definitiv die beste Lösung, aber für manche mit dem sehr umfangreichen regulären Ausdruck könnte es etwas überwältigend sein. Warum nicht als Teil 2 und Teil 1 nur mit den Fips und Zeilenspalten?
Little Bobby Tables

2
@josh das ist ein guter Punkt, während die einzelnen Teile der Regex "leicht" zu verstehen sind, kann lange Regex schnell kompliziert werden. Ich habe einige Erklärungen für zukünftige Leser hinzugefügt! (Ich musste auch den Link zu den Dokumenten aktualisieren, der die (?P<label>...)Syntax erklärt ! Ich habe keine Ahnung, warum ich mich für die komplexere Regex entschieden habe, klar, die einfache könnte funktionieren hmmmm
Andy Hayden

1
Sieht viel freundlicher aus. Ich bin froh, dass du es getan hast, weil ich mir die Dokumente angesehen habe, um das zu verstehen <group_name>. Jetzt weiß ich, dass mein Code dadurch sehr prägnant wird.
Little Bobby Tables


22

Wenn Sie keinen neuen Datenrahmen erstellen möchten oder wenn Ihr Datenrahmen mehr Spalten als nur die Spalten enthält, die Sie teilen möchten, können Sie:

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

1
Ich bekomme eine zip argument #1 must support iterationFehlermeldung, Python 2.7
Allan Ruin

20

Sie können str.splitdurch Leerzeichen (Standardtrennzeichen) und Parameter expand=Truefür das DataFrameZuweisen zu neuen Spalten verwenden:

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

Änderung bei Bedarf Originalspalte mit entfernen 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

Wie ist das gleiche wie:

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

Wenn Fehler auftreten:

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

ValueError: Spalten müssen dieselbe Länge wie der Schlüssel haben

Sie können überprüfen und es gibt 4 Spalten zurück DataFrame, nicht nur 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

Dann wird die Lösung neu angehängt DataFramedurch 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

Mit Originalspalte entfernen (falls noch weitere Spalten vorhanden sind):

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

Wenn Sie eine Zeichenfolge basierend auf einem Trennzeichen in mehr als zwei Spalten aufteilen möchten, können Sie den Parameter 'Maximum Splits' weglassen.
Sie können verwenden:

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

Dadurch werden automatisch so viele Spalten erstellt, wie die maximale Anzahl von Feldern in einer Ihrer anfänglichen Zeichenfolgen enthalten ist.


6

Überrascht habe ich diesen noch nicht gesehen. Wenn Sie nur zwei Teilungen benötigen , kann ich nur empfehlen. . .

Series.str.partition

partition führt einen Split am Separator durch und ist im Allgemeinen ziemlich performant.

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

Wenn Sie die Zeilen umbenennen müssen,

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

Wenn Sie dies wieder mit dem Original verbinden müssen, verwenden Sie joinoder 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

Ich ziehe den Export die entsprechende Pandas Serie (dh die Spalten ich brauche), indem die Anwendung Funktion den Spalteninhalt in mehrere Reihen zu spalten und dann kommt die erzeugten Spalten an den vorhandenen Datenrahmen. Natürlich sollte die Quellenspalte entfernt werden.

z.B

 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)

Um zwei Wörter zu teilen, sollte die Strings-Funktion ungefähr so ​​aussehen:

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

0

Ich habe gesehen, dass niemand die Slice-Methode angewendet hat, also habe ich hier meine 2 Cent hier abgelegt.

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

Diese Methode erstellt zwei neue Spalten.


Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.