359 Stimmen

Teilen (aufteilen) Sie den Zeichenfolgeneintrag des Pandas-Datenrahmens in separate Zeilen auf

Ich habe ein Pandas DataFrame, in dem eine Spalte mit Textzeichenfolgen Komma-getrennte Werte enthält. Ich möchte jedes CSV-Feld aufteilen und für jeden Eintrag eine neue Zeile erstellen (angenommen, dass das CSV sauber ist und nur auf ',' aufgeteilt werden muss). Zum Beispiel sollte a zu b werden:

In [7]: a
Out[7]: 
    var1  var2
0  a,b,c     1
1  d,e,f     2

In [8]: b
Out[8]: 
  var1  var2
0    a     1
1    b     1
2    c     1
3    d     2
4    e     2
5    f     2

Bisher habe ich verschiedene einfache Funktionen ausprobiert, aber die Methode .apply scheint nur einen Zeilenwert als Rückgabewert zu akzeptieren, wenn sie auf einer Achse verwendet wird, und ich kann .transform nicht zum Laufen bringen. Über jede Hilfe würde ich mich freuen!

Beispieldaten:

from pandas import DataFrame
import numpy as np
a = DataFrame([{'var1': 'a,b,c', 'var2': 1},
               {'var1': 'd,e,f', 'var2': 2}])
b = DataFrame([{'var1': 'a', 'var2': 1},
               {'var1': 'b', 'var2': 1},
               {'var1': 'c', 'var2': 1},
               {'var1': 'd', 'var2': 2},
               {'var1': 'e', 'var2': 2},
               {'var1': 'f', 'var2': 2}])

Ich weiß, dass dies nicht funktioniert, weil wir durch die Verwendung von NumPy die Meta-Daten des DataFrame verlieren, aber es sollte Ihnen eine Vorstellung davon geben, was ich zu tun versucht habe:

def fun(row):
    letters = row['var1']
    letters = letters.split(',')
    out = np.array([row] * len(letters))
    out['var1'] = letters
a['idx'] = range(a.shape[0])
z = a.groupby('idx')
z.transform(fun)

3 Stimmen

Andere Lösungen auf dieser Seite funktionieren, aber ich fand die folgende kurz und effektiv. stackoverflow.com/questions/27263805/…

2 Stimmen

Für andere, die diese Seite besuchen und nach einer Lösung suchen, die mehrere Spalten beibehält, werfen Sie einen Blick auf diese Frage: stackoverflow.com/questions/17116814/…

4voto

krassowski Punkte 10262

Ich habe mit dem Out-of-Memory-Erlebnis gekämpft, verschiedene Möglichkeiten ausprobiert, um meine Listen zu explodieren, und einige Benchmarks vorbereitet, um mir bei der Entscheidung zu helfen, welche Antworten ich upvoten soll. Ich habe fünf Szenarien mit unterschiedlichen Verhältnissen von Listenlänge zu Anzahl der Listen getestet. Hier sind die Ergebnisse:

Zeit: (weniger ist besser, hier klicken, um die Großansicht anzuzeigen)

Speed

Spitzen-Arbeitsspeichernutzung: (weniger ist besser)

Peak memory usage

Schlussfolgerungen:

  • @MaxU's answer (Update 2), Deckname verketten bietet die beste Geschwindigkeit in fast jedem Fall, während die Spitzen-Arbeitsspeichernutzung niedrig gehalten wird,
  • sehen Sie sich @DMulligan's answer (Deckname stack) an, wenn Sie viele Zeilen mit relativ kleinen Listen verarbeiten müssen und einen erhöhten Spitzen-Arbeitsspeicher in Kauf nehmen können,
  • die angenommene @Chang's answer funktioniert gut für Datenrahmen, die nur wenige Zeilen haben, aber sehr große Listen.

Alle Details (Funktionen und Benchmarking-Code) finden Sie in diesem GitHub-Gist. Beachten Sie bitte, dass das Benchmark-Problem vereinfacht wurde und das Aufteilen von Strings in die Liste nicht enthielt - wie die meisten Lösungen in ähnlicher Weise durchgeführt haben.

0 Stimmen

Schöner Vergleich! Könnten Sie den Code posten, den Sie zum Plotten der Benchmarks verwendet haben?

1 Stimmen

Bitte sehen Sie sich diesen Link an: gist.github.com/krassowski/0259a2cd2ba774ccd9f69bbcc3187fbf (bereits in der Antwort enthalten) - Meiner Meinung nach wäre es etwas zu lang, um es hier komplett einzufügen.

4voto

1'' Punkte 25004

Einzeiler mit split(___, expand=True) und den level und name Argumenten für reset_index():

>>> b = a.var1.str.split(',', expand=True).set_index(a.var2).stack().reset_index(level=0, name='var1')
>>> b
   var2 var1
0     1    a
1     1    b
2     1    c
0     2    d
1     2    e
2     2    f

Wenn du möchtest, dass b genauso aussieht wie in der Frage, kannst du zusätzlich Folgendes tun:

>>> b = b.reset_index(drop=True)[['var1', 'var2']]
>>> b
  var1  var2
0    a     1
1    b     1
2    c     1
3    d     2
4    e     2
5    f     2

2voto

Dennis Golomazov Punkte 14467

Basierend auf der ausgezeichneten Lösung von @DMulligan's Lösung gibt es hier eine generische vektorisierte (keine Schleifen) Funktion, die eine Spalte eines Dataframes in mehrere Zeilen aufteilt und sie wieder mit dem Original-Dataframe zusammenführt. Es verwendet auch eine großartige generische change_column_order Funktion aus dieser Antwort.

def change_column_order(df, col_name, index):
    cols = df.columns.tolist()
    cols.remove(col_name)
    cols.insert(index, col_name)
    return df[cols]

def split_df(dataframe, col_name, sep):
    orig_col_index = dataframe.columns.tolist().index(col_name)
    orig_index_name = dataframe.index.name
    orig_columns = dataframe.columns
    dataframe = dataframe.reset_index()  # wir brauchen einen natürlichen Index ab 0 für den Zusammenführungsvorgang
    index_col_name = (set(dataframe.columns) - set(orig_columns)).pop()
    df_split = pd.DataFrame(
        pd.DataFrame(dataframe[col_name].str.split(sep).tolist())
        .stack().reset_index(level=1, drop=1), columns=[col_name])
    df = dataframe.drop(col_name, axis=1)
    df = pd.merge(df, df_split, left_index=True, right_index=True, how='inner')
    df = df.set_index(index_col_name)
    df.index.name = orig_index_name
    # merge fügt die Spalte an letzter Stelle hinzu, daher müssen wir sie zurückverschieben
    return change_column_order(df, col_name, orig_col_index)

Beispiel:

df = pd.DataFrame([['a:b', 1, 4], ['c:d', 2, 5], ['e:f:g:h', 3, 6]], 
                  columns=['Name', 'A', 'B'], index=[10, 12, 13])
df
        Name    A   B
    10   a:b     1   4
    12   c:d     2   5
    13   e:f:g:h 3   6

split_df(df, 'Name', ':')
    Name    A   B
10   a       1   4
10   b       1   4
12   c       2   5
12   d       2   5
13   e       3   6
13   f       3   6    
13   g       3   6    
13   h       3   6    

Beachten Sie, dass der ursprüngliche Index und die Reihenfolge der Spalten erhalten bleiben. Es funktioniert auch mit Dataframes, die einen nicht-sequenziellen Index haben.

2 Stimmen

Das hat mir geholfen, gute Arbeit: stackoverflow.com/a/48554655/6672746

2voto

cgels Punkte 43

Die String-Funktion split kann ein optionales boolesches Argument 'expand' annehmen.

Hier ist eine Lösung unter Verwendung dieses Arguments:

(a.var1
  .str.split(",",expand=True)
  .set_index(a.var2)
  .stack()
  .reset_index(level=1, drop=True)
  .reset_index()
  .rename(columns={0:"var1"}))

2voto

Mykola Zotko Punkte 11977

Einzelliniger Code unter Verwendung von assign und explode:

    col1  col2
0  a,b,c     1
1  d,e,f     2

df.assign(col1 = df.col1.str.split(',')).explode('col1', ignore_index=True)

Ergebnis:

  col1  col2
0    a     1
1    b     1
2    c     1
3    d     2
4    e     2
5    f     2

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X