Pandas teilen die Listenspalte in mehrere Spalten auf

136

Ich habe einen Pandas DataFrame mit einer Spalte:

import pandas as pd

df = pd.DataFrame(
    data={
        "teams": [
            ["SF", "NYG"],
            ["SF", "NYG"],
            ["SF", "NYG"],
            ["SF", "NYG"],
            ["SF", "NYG"],
            ["SF", "NYG"],
            ["SF", "NYG"],
        ]
    }
)

print(df)

Ausgabe:

       teams
0  [SF, NYG]
1  [SF, NYG]
2  [SF, NYG]
3  [SF, NYG]
4  [SF, NYG]
5  [SF, NYG]
6  [SF, NYG]

Wie kann diese Listenspalte in zwei Spalten aufgeteilt werden?

user2938093
quelle

Antworten:

243

Sie können den DataFrameKonstruktor verwenden mit listserstellt von to_list:

import pandas as pd

d1 = {'teams': [['SF', 'NYG'],['SF', 'NYG'],['SF', 'NYG'],
                ['SF', 'NYG'],['SF', 'NYG'],['SF', 'NYG'],['SF', 'NYG']]}
df2 = pd.DataFrame(d1)
print (df2)
       teams
0  [SF, NYG]
1  [SF, NYG]
2  [SF, NYG]
3  [SF, NYG]
4  [SF, NYG]
5  [SF, NYG]
6  [SF, NYG]

df2[['team1','team2']] = pd.DataFrame(df2.teams.tolist(), index= df2.index)
print (df2)
       teams team1 team2
0  [SF, NYG]    SF   NYG
1  [SF, NYG]    SF   NYG
2  [SF, NYG]    SF   NYG
3  [SF, NYG]    SF   NYG
4  [SF, NYG]    SF   NYG
5  [SF, NYG]    SF   NYG
6  [SF, NYG]    SF   NYG

Und für neue DataFrame:

df3 = pd.DataFrame(df2['teams'].to_list(), columns=['team1','team2'])
print (df3)
  team1 team2
0    SF   NYG
1    SF   NYG
2    SF   NYG
3    SF   NYG
4    SF   NYG
5    SF   NYG
6    SF   NYG

Lösung mit apply(pd.Series)ist sehr langsam:

#7k rows
df2 = pd.concat([df2]*1000).reset_index(drop=True)

In [121]: %timeit df2['teams'].apply(pd.Series)
1.79 s ± 52.5 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)

In [122]: %timeit pd.DataFrame(df2['teams'].to_list(), columns=['team1','team2'])
1.63 ms ± 54.3 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
jezrael
quelle
4
Kleinere Einschränkung: Wenn Sie es für einen vorhandenen Datenrahmen verwenden, stellen Sie sicher, dass der Index zurückgesetzt wird, da er sonst nicht korrekt zugewiesen wird.
user1700890
1
@ user1700890 - ja, oder geben Sie den Index im DataFrame-Konstruktor andf2[['team1','team2']] = pd.DataFrame(df2.teams.values.tolist(), index= df2.index)
jezrael
1
@Catbuilts - ja, wenn es eine Vektorisierungslösung gibt, vermeiden Sie sie am besten.
Jezrael
1
@Catbuilts - ja natürlich. Vektorisiert bedeutet im Allgemeinen keine Schleifen, also keine Anwendung, kein For, kein Listenverständnis. Aber es kommt darauf an, was genau benötigt wird. Vielleicht helfen auch diese
jezrael
2
@Catbuilts Zwar ist es apply()möglicherweise langsamer, aber es ist die Go-to-Methode, wenn Eingabezeichenfolge und Werte in den Zeilen der Originalserie nicht gleich sind!
CheTesta
52

Viel einfachere Lösung:

pd.DataFrame(df2["teams"].to_list(), columns=['team1', 'team2'])

Erträge,

  team1 team2
-------------
0    SF   NYG
1    SF   NYG
2    SF   NYG
3    SF   NYG
4    SF   NYG
5    SF   NYG
6    SF   NYG
7    SF   NYG

Wenn Sie eine Spalte mit begrenzten Zeichenfolgen anstelle von Listen teilen möchten, können Sie Folgendes tun:

pd.DataFrame(df["teams"].str.split('<delim>', expand=True).values,
             columns=['team1', 'team2'])
Joseph Davison
quelle
5
Was ist, wenn jede Liste eine ungerade Anzahl von Elementen enthält?
Ikel
Wenn Sie eine Spalte mit begrenzten Zeichenfolgen anstelle von Listen df["teams"].str.split('<delim>', expand=True) teilen möchten , können Sie dies auf ähnliche Weise tun: Gibt bereits einen DataFrame zurück, sodass es wahrscheinlich einfacher wäre, die Spalten einfach umzubenennen.
AMC
26

Diese Lösung df2behält den Index des DataFrame bei, im Gegensatz zu jeder Lösung, die Folgendes verwendet tolist():

df3 = df2.teams.apply(pd.Series)
df3.columns = ['team1', 'team2']

Hier ist das Ergebnis:

  team1 team2
0    SF   NYG
1    SF   NYG
2    SF   NYG
3    SF   NYG
4    SF   NYG
5    SF   NYG
6    SF   NYG
Kevin Markham
quelle
2
Auch eine der langsamsten, die applySie in Pandas tun können. Sie sollten diese Methode vermeiden und die akzeptierte Antwort verwenden. In den Zeiten der Top-Antwort ist diese Methode ca. 1400 xlangsamer @rajan
Erfan
2
@Erfan Ja, aber manchmal ist es dem Benutzer egal, ob eine Operation 1s oder 1ms dauert, und stattdessen ist es ihm am wichtigsten, den einfachsten und am besten lesbaren Code zu schreiben! Ich erkenne an, dass Lesbarkeit / Einfachheit subjektiv ist, aber mein Punkt ist einfach, dass Geschwindigkeit nicht für alle Benutzer zu jeder Zeit eine Priorität ist.
Kevin Markham
1
Darüber hinaus fand ich heraus, dass die applyMethode zuverlässiger zum Erweitern großer Arrays (über 1000 Elemente) in großen Datenmengen funktioniert. Die tolist()Methode hat meinen Prozess abgebrochen, als der Datensatz 500.000 Zeilen überschritten hat.
Moritz
2
Dies ist eine großartige Lösung, da sie gut mit Listen unterschiedlicher Größe funktioniert.
Dasilvadaniel
@ KevinMarkham Sie kümmern sich am meisten darum, den einfachsten und am besten lesbaren Code zu schreiben. Ist das pd.DataFrame(df["teams"].to_list(), columns=["team_1", "team_2"])wirklich so viel komplizierter?
AMC
15

Es scheint einen syntaktisch einfacheren und daher leichter zu merkenden Weg zu geben als die vorgeschlagenen Lösungen. Ich gehe davon aus, dass die Spalte in einem Datenrahmen df 'meta' heißt:

df2 = pd.DataFrame(df['meta'].str.split().values.tolist())
Mikkokotila
quelle
1
Ich habe einen Fehler erhalten, den ich jedoch durch Entfernen des Fehlers behoben habe str.split(). Dies war viel einfacher und hat den Vorteil, wenn Sie die Anzahl der Elemente in Ihrer Liste nicht kennen.
otteheng
Es scheint einen syntaktisch einfacheren und daher leichter zu merkenden Weg zu geben als die vorgeschlagenen Lösungen. "Ja wirklich?" Denn dies ist praktisch identisch mit der Top-Antwort, die Jahre zuvor veröffentlicht wurde. Der einzige Unterschied ist der Teil, der nicht mit dieser speziellen Frage zusammenhängt.
AMC
Es funktioniert bei mir !!
EduardoUstarez
3

Basierend auf den vorherigen Antworten ist hier eine andere Lösung, die das gleiche Ergebnis wie df2.teams.apply (pd.Series) mit einer viel schnelleren Laufzeit zurückgibt:

pd.DataFrame([{x: y for x, y in enumerate(item)} for item in df2['teams'].values.tolist()], index=df2.index)

Timings:

In [1]:
import pandas as pd
d1 = {'teams': [['SF', 'NYG'],['SF', 'NYG'],['SF', 'NYG'],
                ['SF', 'NYG'],['SF', 'NYG'],['SF', 'NYG'],['SF', 'NYG']]}
df2 = pd.DataFrame(d1)
df2 = pd.concat([df2]*1000).reset_index(drop=True)

In [2]: %timeit df2['teams'].apply(pd.Series)

8.27 s ± 2.73 s per loop (mean ± std. dev. of 7 runs, 1 loop each)

In [3]: %timeit pd.DataFrame([{x: y for x, y in enumerate(item)} for item in df2['teams'].values.tolist()], index=df2.index)

35.4 ms ± 5.22 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)
ailurid
quelle
3

Die oben genannten Lösungen haben bei mir nicht funktioniert, da ich nanBeobachtungen in meinem habe dataframe. In meinem Fall df2[['team1','team2']] = pd.DataFrame(df2.teams.values.tolist(), index= df2.index)ergibt:

object of type 'float' has no len()

Ich löse dies mit Listenverständnis. Hier das replizierbare Beispiel:

import pandas as pd
import numpy as np
d1 = {'teams': [['SF', 'NYG'],['SF', 'NYG'],['SF', 'NYG'],
            ['SF', 'NYG'],['SF', 'NYG'],['SF', 'NYG'],['SF', 'NYG']]}
df2 = pd.DataFrame(d1)
df2.loc[2,'teams'] = np.nan
df2.loc[4,'teams'] = np.nan
df2

Ausgabe:

        teams
0   [SF, NYG]
1   [SF, NYG]
2   NaN
3   [SF, NYG]
4   NaN
5   [SF, NYG]
6   [SF, NYG]

df2['team1']=np.nan
df2['team2']=np.nan

Lösen mit Listenverständnis:

for i in [0,1]:
    df2['team{}'.format(str(i+1))]=[k[i] if isinstance(k,list) else k for k in df2['teams']]

df2

Ausbeuten:

    teams   team1   team2
0   [SF, NYG]   SF  NYG
1   [SF, NYG]   SF  NYG
2   NaN        NaN  NaN
3   [SF, NYG]   SF  NYG
4   NaN        NaN  NaN
5   [SF, NYG]   SF  NYG
6   [SF, NYG]   SF  NYG
Lucas
quelle
1

Listenverständnis

einfache Implementierung mit Listenverständnis (mein Favorit)

df = pd.DataFrame([pd.Series(x) for x in df.teams])
df.columns = ['team_{}'.format(x+1) for x in df.columns]

Timing am Ausgang:

CPU times: user 0 ns, sys: 0 ns, total: 0 ns
Wall time: 2.71 ms

Ausgabe:

team_1  team_2
0   SF  NYG
1   SF  NYG
2   SF  NYG
3   SF  NYG
4   SF  NYG
5   SF  NYG
6   SF  NYG
Talis
quelle
Diese Art der Behandlung von Listen unterschiedlicher Länge - eine Verbesserung gegenüber vielen anderen Antworten, führt jedoch dazu, dass sich die Elemente nicht in ihren eigenen Spalten befinden.
Isaac